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>
320fe554a4SStephen Hemminger #include <linux/ucs2_string.h>
3395fa0405SHaiyang Zhang 
3495fa0405SHaiyang Zhang #include "hyperv_net.h"
35ec966381SStephen Hemminger #include "netvsc_trace.h"
3695fa0405SHaiyang Zhang 
374f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w);
3895fa0405SHaiyang Zhang 
395b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
4095fa0405SHaiyang Zhang struct rndis_request {
4195fa0405SHaiyang Zhang 	struct list_head list_ent;
4295fa0405SHaiyang Zhang 	struct completion  wait_event;
4395fa0405SHaiyang Zhang 
4495fa0405SHaiyang Zhang 	struct rndis_message response_msg;
45a3a6cab5SHaiyang Zhang 	/*
46a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
47a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
48a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
49a3a6cab5SHaiyang Zhang 	 * future.
50a3a6cab5SHaiyang Zhang 	 */
51a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
5295fa0405SHaiyang Zhang 
5395fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
5495fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
550f48917bSHaiyang Zhang 
5695fa0405SHaiyang Zhang 	struct rndis_message request_msg;
570f48917bSHaiyang Zhang 	/*
58a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
59a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
600f48917bSHaiyang Zhang 	 */
61a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
6295fa0405SHaiyang Zhang };
6395fa0405SHaiyang Zhang 
64962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
65962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
66962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
67962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
68962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
69962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
70962f3feeSstephen hemminger };
71962f3feeSstephen hemminger 
7295fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
7395fa0405SHaiyang Zhang {
7495fa0405SHaiyang Zhang 	struct rndis_device *device;
7595fa0405SHaiyang Zhang 
7695fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
7795fa0405SHaiyang Zhang 	if (!device)
7895fa0405SHaiyang Zhang 		return NULL;
7995fa0405SHaiyang Zhang 
8095fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
8195fa0405SHaiyang Zhang 
8295fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
834f19c0d8Sstephen hemminger 	INIT_WORK(&device->mcast_work, rndis_set_multicast);
8495fa0405SHaiyang Zhang 
8595fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
8695fa0405SHaiyang Zhang 
8795fa0405SHaiyang Zhang 	return device;
8895fa0405SHaiyang Zhang }
8995fa0405SHaiyang Zhang 
9095fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
9195fa0405SHaiyang Zhang 					     u32 msg_type,
9295fa0405SHaiyang Zhang 					     u32 msg_len)
9395fa0405SHaiyang Zhang {
9495fa0405SHaiyang Zhang 	struct rndis_request *request;
9595fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
9695fa0405SHaiyang Zhang 	struct rndis_set_request *set;
9795fa0405SHaiyang Zhang 	unsigned long flags;
9895fa0405SHaiyang Zhang 
9995fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
10095fa0405SHaiyang Zhang 	if (!request)
10195fa0405SHaiyang Zhang 		return NULL;
10295fa0405SHaiyang Zhang 
10395fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
10495fa0405SHaiyang Zhang 
10595fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
10695fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
10795fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
10895fa0405SHaiyang Zhang 
1095b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1105b54dac8SHaiyang Zhang 
11195fa0405SHaiyang Zhang 	/*
11295fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
11395fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
11495fa0405SHaiyang Zhang 	 * template
11595fa0405SHaiyang Zhang 	 */
11695fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
11795fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
11895fa0405SHaiyang Zhang 
11995fa0405SHaiyang Zhang 	/* Add to the request list */
12095fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12195fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
12295fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12395fa0405SHaiyang Zhang 
12495fa0405SHaiyang Zhang 	return request;
12595fa0405SHaiyang Zhang }
12695fa0405SHaiyang Zhang 
12795fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
12895fa0405SHaiyang Zhang 			    struct rndis_request *req)
12995fa0405SHaiyang Zhang {
13095fa0405SHaiyang Zhang 	unsigned long flags;
13195fa0405SHaiyang Zhang 
13295fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
13395fa0405SHaiyang Zhang 	list_del(&req->list_ent);
13495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
13595fa0405SHaiyang Zhang 
13695fa0405SHaiyang Zhang 	kfree(req);
13795fa0405SHaiyang Zhang }
13895fa0405SHaiyang Zhang 
13979cf1baeSStephen Hemminger static void dump_rndis_message(struct net_device *netdev,
140dc54a08cSstephen hemminger 			       const struct rndis_message *rndis_msg)
14195fa0405SHaiyang Zhang {
14295fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
14351491167SLinus Walleij 	case RNDIS_MSG_PACKET:
14451491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14595fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14695fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14795fa0405SHaiyang Zhang 			   "pkt len %u\n",
14895fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
15095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
15195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
15295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
15395fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
15495fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
15595fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15695fa0405SHaiyang Zhang 		break;
15795fa0405SHaiyang Zhang 
15851491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15951491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
16095fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
16195fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
16295fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
16395fa0405SHaiyang Zhang 			rndis_msg->msg_len,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16895fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
17095fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17195fa0405SHaiyang Zhang 			   max_pkt_per_msg,
17295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17395fa0405SHaiyang Zhang 			   pkt_alignment_factor);
17495fa0405SHaiyang Zhang 		break;
17595fa0405SHaiyang Zhang 
17651491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17751491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17895fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17995fa0405SHaiyang Zhang 			"buf offset %u)\n",
18095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
18195fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
18295fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
18395fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18495fa0405SHaiyang Zhang 			   info_buflen,
18595fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18695fa0405SHaiyang Zhang 			   info_buf_offset);
18795fa0405SHaiyang Zhang 		break;
18895fa0405SHaiyang Zhang 
18951491167SLinus Walleij 	case RNDIS_MSG_SET_C:
19095fa0405SHaiyang Zhang 		netdev_dbg(netdev,
19151491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19295fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19395fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
19495fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
19595fa0405SHaiyang Zhang 		break;
19695fa0405SHaiyang Zhang 
19751491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19851491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19995fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
20095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
20195fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
20295fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
20395fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
20495fa0405SHaiyang Zhang 		break;
20595fa0405SHaiyang Zhang 
20695fa0405SHaiyang Zhang 	default:
20795fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20895fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20995fa0405SHaiyang Zhang 			rndis_msg->msg_len);
21095fa0405SHaiyang Zhang 		break;
21195fa0405SHaiyang Zhang 	}
21295fa0405SHaiyang Zhang }
21395fa0405SHaiyang Zhang 
21495fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
21595fa0405SHaiyang Zhang 				  struct rndis_request *req)
21695fa0405SHaiyang Zhang {
21795fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
218b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
219a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
22002b6de01Sstephen hemminger 	int ret;
22195fa0405SHaiyang Zhang 
22295fa0405SHaiyang Zhang 	/* Setup the packet to send it */
22395fa0405SHaiyang Zhang 	packet = &req->pkt;
22495fa0405SHaiyang Zhang 
22595fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
22695fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22795fa0405SHaiyang Zhang 
228a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
22995fa0405SHaiyang Zhang 					PAGE_SHIFT;
230a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
231a9f2e2d6SKY Srinivasan 	pb[0].offset =
23295fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
23395fa0405SHaiyang Zhang 
23499e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
235a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
23699e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
237a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
238a9f2e2d6SKY Srinivasan 			pb[0].offset;
239a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
240a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
241a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
242a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
243a9f2e2d6SKY Srinivasan 			pb[0].len;
24499e3fcfaSHaiyang Zhang 	}
24599e3fcfaSHaiyang Zhang 
246ec966381SStephen Hemminger 	trace_rndis_send(dev->ndev, 0, &req->request_msg);
247ec966381SStephen Hemminger 
248867047c4Sstephen hemminger 	rcu_read_lock_bh();
249cfd8afd9SStephen Hemminger 	ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL);
250867047c4Sstephen hemminger 	rcu_read_unlock_bh();
251867047c4Sstephen hemminger 
25295fa0405SHaiyang Zhang 	return ret;
25395fa0405SHaiyang Zhang }
25495fa0405SHaiyang Zhang 
2551b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2561b07da51SHaiyang Zhang 				 struct rndis_request *request)
2571b07da51SHaiyang Zhang {
2581b07da51SHaiyang Zhang 	u32 link_status;
2591b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2601b07da51SHaiyang Zhang 
2611b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2621b07da51SHaiyang Zhang 
2631b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2641b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2651b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2661b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2671b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2681b07da51SHaiyang Zhang 	}
2691b07da51SHaiyang Zhang }
2701b07da51SHaiyang Zhang 
27102400fceSStephen Hemminger static void rndis_filter_receive_response(struct net_device *ndev,
27202400fceSStephen Hemminger 					  struct netvsc_device *nvdev,
27302400fceSStephen Hemminger 					  const struct rndis_message *resp)
27495fa0405SHaiyang Zhang {
27502400fceSStephen Hemminger 	struct rndis_device *dev = nvdev->extension;
27695fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
27795fa0405SHaiyang Zhang 	bool found = false;
27895fa0405SHaiyang Zhang 	unsigned long flags;
27902400fceSStephen Hemminger 
28002400fceSStephen Hemminger 	/* This should never happen, it means control message
28102400fceSStephen Hemminger 	 * response received after device removed.
28202400fceSStephen Hemminger 	 */
28302400fceSStephen Hemminger 	if (dev->state == RNDIS_DEV_UNINITIALIZED) {
28402400fceSStephen Hemminger 		netdev_err(ndev,
28502400fceSStephen Hemminger 			   "got rndis message uninitialized\n");
28602400fceSStephen Hemminger 		return;
28702400fceSStephen Hemminger 	}
28895fa0405SHaiyang Zhang 
28995fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
29095fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
29195fa0405SHaiyang Zhang 		/*
29295fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
29395fa0405SHaiyang Zhang 		 * field
29495fa0405SHaiyang Zhang 		 */
29595fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
29695fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
29795fa0405SHaiyang Zhang 			found = true;
29895fa0405SHaiyang Zhang 			break;
29995fa0405SHaiyang Zhang 		}
30095fa0405SHaiyang Zhang 	}
30195fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
30295fa0405SHaiyang Zhang 
30395fa0405SHaiyang Zhang 	if (found) {
304a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
305a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
30695fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
30795fa0405SHaiyang Zhang 			       resp->msg_len);
3081b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
3091b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
3101b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
3111b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
31295fa0405SHaiyang Zhang 		} else {
31395fa0405SHaiyang Zhang 			netdev_err(ndev,
31495fa0405SHaiyang Zhang 				"rndis response buffer overflow "
31595fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
31695fa0405SHaiyang Zhang 				resp->msg_len,
31786eedaccSKY Srinivasan 				sizeof(struct rndis_message));
31895fa0405SHaiyang Zhang 
31995fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
32051491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
32195fa0405SHaiyang Zhang 				/* does not have a request id field */
32295fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
323007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
32495fa0405SHaiyang Zhang 			} else {
32595fa0405SHaiyang Zhang 				request->response_msg.msg.
32695fa0405SHaiyang Zhang 				init_complete.status =
327007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
32895fa0405SHaiyang Zhang 			}
32995fa0405SHaiyang Zhang 		}
33095fa0405SHaiyang Zhang 
33195fa0405SHaiyang Zhang 		complete(&request->wait_event);
33295fa0405SHaiyang Zhang 	} else {
33395fa0405SHaiyang Zhang 		netdev_err(ndev,
33495fa0405SHaiyang Zhang 			"no rndis request found for this response "
33595fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
33695fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
33795fa0405SHaiyang Zhang 			resp->ndis_msg_type);
33895fa0405SHaiyang Zhang 	}
33995fa0405SHaiyang Zhang }
34095fa0405SHaiyang Zhang 
3411f5f3a75SHaiyang Zhang /*
3421f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3431f5f3a75SHaiyang Zhang  * return NULL if not found.
3441f5f3a75SHaiyang Zhang  */
3451f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3461f5f3a75SHaiyang Zhang {
3471f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3481f5f3a75SHaiyang Zhang 	int len;
3491f5f3a75SHaiyang Zhang 
3501f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3511f5f3a75SHaiyang Zhang 		return NULL;
3521f5f3a75SHaiyang Zhang 
3531f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3541f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3551f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3561f5f3a75SHaiyang Zhang 
3571f5f3a75SHaiyang Zhang 	while (len > 0) {
3581f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3591f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3601f5f3a75SHaiyang Zhang 		len -= ppi->size;
3611f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3621f5f3a75SHaiyang Zhang 	}
3631f5f3a75SHaiyang Zhang 
3641f5f3a75SHaiyang Zhang 	return NULL;
3651f5f3a75SHaiyang Zhang }
3661f5f3a75SHaiyang Zhang 
367dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
368345ac089SStephen Hemminger 				     struct netvsc_device *nvdev,
369dc54a08cSstephen hemminger 				     struct vmbus_channel *channel,
3703be9b5fdSHaiyang Zhang 				     struct rndis_message *msg,
3713be9b5fdSHaiyang Zhang 				     u32 data_buflen)
37295fa0405SHaiyang Zhang {
373dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
374dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
375dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
37695fa0405SHaiyang Zhang 	u32 data_offset;
3773be9b5fdSHaiyang Zhang 	void *data;
37895fa0405SHaiyang Zhang 
37995fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
38095fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
38195fa0405SHaiyang Zhang 
382dc54a08cSstephen hemminger 	data_buflen -= data_offset;
3834b8a8bc9SWei Yongjun 
3844b8a8bc9SWei Yongjun 	/*
3854b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3864b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3874b8a8bc9SWei Yongjun 	 */
388dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
38902400fceSStephen Hemminger 		netdev_err(ndev, "rndis message buffer "
3904b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3914b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
392dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
39310082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3944b8a8bc9SWei Yongjun 	}
3954b8a8bc9SWei Yongjun 
396dc54a08cSstephen hemminger 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
397dc54a08cSstephen hemminger 
3983be9b5fdSHaiyang Zhang 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
3993be9b5fdSHaiyang Zhang 
4003be9b5fdSHaiyang Zhang 	data = (void *)msg + data_offset;
4013be9b5fdSHaiyang Zhang 
4024b8a8bc9SWei Yongjun 	/*
4034b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
4044b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
4054b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
4064b8a8bc9SWei Yongjun 	 */
407345ac089SStephen Hemminger 	return netvsc_recv_callback(ndev, nvdev, channel,
408dc54a08cSstephen hemminger 				    data, rndis_pkt->data_len,
409dc54a08cSstephen hemminger 				    csum_info, vlan);
41095fa0405SHaiyang Zhang }
41195fa0405SHaiyang Zhang 
412dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
413dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
414dc54a08cSstephen hemminger 			 struct vmbus_channel *channel,
415dc54a08cSstephen hemminger 			 void *data, u32 buflen)
41695fa0405SHaiyang Zhang {
4173d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
418dc54a08cSstephen hemminger 	struct rndis_message *rndis_msg = data;
41995fa0405SHaiyang Zhang 
420dc54a08cSstephen hemminger 	if (netif_msg_rx_status(net_device_ctx))
42179cf1baeSStephen Hemminger 		dump_rndis_message(ndev, rndis_msg);
42295fa0405SHaiyang Zhang 
423ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
42451491167SLinus Walleij 	case RNDIS_MSG_PACKET:
4253be9b5fdSHaiyang Zhang 		return rndis_filter_receive_data(ndev, net_dev, channel,
4263be9b5fdSHaiyang Zhang 						 rndis_msg, buflen);
42751491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
42851491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
42951491167SLinus Walleij 	case RNDIS_MSG_SET_C:
43095fa0405SHaiyang Zhang 		/* completion msgs */
43102400fceSStephen Hemminger 		rndis_filter_receive_response(ndev, net_dev, rndis_msg);
43295fa0405SHaiyang Zhang 		break;
43395fa0405SHaiyang Zhang 
43451491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
43595fa0405SHaiyang Zhang 		/* notification msgs */
43679cf1baeSStephen Hemminger 		netvsc_linkstatus_callback(ndev, rndis_msg);
43795fa0405SHaiyang Zhang 		break;
43895fa0405SHaiyang Zhang 	default:
43995fa0405SHaiyang Zhang 		netdev_err(ndev,
44095fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
441ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
442ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
4435c71dadbSHaiyang Zhang 		return NVSP_STAT_FAIL;
44495fa0405SHaiyang Zhang 	}
44595fa0405SHaiyang Zhang 
4465c71dadbSHaiyang Zhang 	return NVSP_STAT_SUCCESS;
44795fa0405SHaiyang Zhang }
44895fa0405SHaiyang Zhang 
449867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev,
450867047c4Sstephen hemminger 				     struct netvsc_device *nvdev,
451867047c4Sstephen hemminger 				     u32 oid, void *result, u32 *result_size)
45295fa0405SHaiyang Zhang {
45395fa0405SHaiyang Zhang 	struct rndis_request *request;
45495fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
45595fa0405SHaiyang Zhang 	struct rndis_query_request *query;
45695fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
45795fa0405SHaiyang Zhang 	int ret = 0;
45895fa0405SHaiyang Zhang 
45995fa0405SHaiyang Zhang 	if (!result)
46095fa0405SHaiyang Zhang 		return -EINVAL;
46195fa0405SHaiyang Zhang 
46295fa0405SHaiyang Zhang 	*result_size = 0;
46351491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
46495fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
46595fa0405SHaiyang Zhang 	if (!request) {
46695fa0405SHaiyang Zhang 		ret = -ENOMEM;
46795fa0405SHaiyang Zhang 		goto cleanup;
46895fa0405SHaiyang Zhang 	}
46995fa0405SHaiyang Zhang 
47095fa0405SHaiyang Zhang 	/* Setup the rndis query */
47195fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
47295fa0405SHaiyang Zhang 	query->oid = oid;
47395fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
47495fa0405SHaiyang Zhang 	query->info_buflen = 0;
47595fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
47695fa0405SHaiyang Zhang 
47723312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
47823312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
47923312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
48023312a3bSstephen hemminger 		u8 ndis_rev;
48123312a3bSstephen hemminger 		size_t size;
48223312a3bSstephen hemminger 
48323312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
48423312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
48523312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
48623312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
48723312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
48823312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
48923312a3bSstephen hemminger 		} else {
49023312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
49123312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
49223312a3bSstephen hemminger 		}
49323312a3bSstephen hemminger 
49423312a3bSstephen hemminger 		request->request_msg.msg_len += size;
49523312a3bSstephen hemminger 		query->info_buflen = size;
49623312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
49723312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
49823312a3bSstephen hemminger 
49923312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
50023312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
50123312a3bSstephen hemminger 		hwcaps->header.size = size;
50223312a3bSstephen hemminger 
50323312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
5045b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
5055b54dac8SHaiyang Zhang 
5065b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5075b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5085b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5095b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5105b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5115b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5125b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5135b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5145b54dac8SHaiyang Zhang 	}
5155b54dac8SHaiyang Zhang 
51695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
51795fa0405SHaiyang Zhang 	if (ret != 0)
51895fa0405SHaiyang Zhang 		goto cleanup;
51995fa0405SHaiyang Zhang 
5205362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
52195fa0405SHaiyang Zhang 
52295fa0405SHaiyang Zhang 	/* Copy the response back */
52395fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
52495fa0405SHaiyang Zhang 
52595fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
52695fa0405SHaiyang Zhang 		ret = -1;
52795fa0405SHaiyang Zhang 		goto cleanup;
52895fa0405SHaiyang Zhang 	}
52995fa0405SHaiyang Zhang 
53095fa0405SHaiyang Zhang 	memcpy(result,
53195fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
53295fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
53395fa0405SHaiyang Zhang 	       query_complete->info_buflen);
53495fa0405SHaiyang Zhang 
53595fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
53695fa0405SHaiyang Zhang 
53795fa0405SHaiyang Zhang cleanup:
53895fa0405SHaiyang Zhang 	if (request)
53995fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
54095fa0405SHaiyang Zhang 
54195fa0405SHaiyang Zhang 	return ret;
54295fa0405SHaiyang Zhang }
54395fa0405SHaiyang Zhang 
54423312a3bSstephen hemminger /* Get the hardware offload capabilities */
54523312a3bSstephen hemminger static int
546867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device,
547867047c4Sstephen hemminger 		   struct ndis_offload *caps)
54823312a3bSstephen hemminger {
54923312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
55023312a3bSstephen hemminger 	int ret;
55123312a3bSstephen hemminger 
55223312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
55323312a3bSstephen hemminger 
554867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
55523312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
55623312a3bSstephen hemminger 					caps, &caps_len);
55723312a3bSstephen hemminger 	if (ret)
55823312a3bSstephen hemminger 		return ret;
55923312a3bSstephen hemminger 
56023312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
56123312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
56223312a3bSstephen hemminger 			    caps->header.type);
56323312a3bSstephen hemminger 		return -EINVAL;
56423312a3bSstephen hemminger 	}
56523312a3bSstephen hemminger 
56623312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
56723312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
56823312a3bSstephen hemminger 			    caps->header.revision);
56923312a3bSstephen hemminger 		return -EINVAL;
57023312a3bSstephen hemminger 	}
57123312a3bSstephen hemminger 
57223312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
57323312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
57423312a3bSstephen hemminger 		netdev_warn(dev->ndev,
57523312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
57623312a3bSstephen hemminger 			    caps->header.size, caps_len);
57723312a3bSstephen hemminger 		return -EINVAL;
57823312a3bSstephen hemminger 	}
57923312a3bSstephen hemminger 
58023312a3bSstephen hemminger 	return 0;
58123312a3bSstephen hemminger }
58223312a3bSstephen hemminger 
583867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev,
584867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
58595fa0405SHaiyang Zhang {
58695fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
58795fa0405SHaiyang Zhang 
588867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
58995fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
59095fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
59195fa0405SHaiyang Zhang }
59295fa0405SHaiyang Zhang 
5931ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5941ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5951ce09e89SHaiyang Zhang 
596867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev,
597867047c4Sstephen hemminger 				const char *mac)
5981ce09e89SHaiyang Zhang {
5991ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
6001ce09e89SHaiyang Zhang 	struct rndis_request *request;
6011ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
6021ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
6031ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
6041ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
6051ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6061ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6071ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
608999028ccSNicholas Mc Guire 	int ret;
6091ce09e89SHaiyang Zhang 
6101ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6111ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6121ce09e89SHaiyang Zhang 	if (!request)
6131ce09e89SHaiyang Zhang 		return -ENOMEM;
6141ce09e89SHaiyang Zhang 
6151ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6161ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6171ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6181ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6191ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6201ce09e89SHaiyang Zhang 
6211ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6221ce09e89SHaiyang Zhang 		set->info_buf_offset);
6231ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6241ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6251ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6261ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6271ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6281ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6291ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6301ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6311ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6321ce09e89SHaiyang Zhang 
6331ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6341ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6351ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6361ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6371ce09e89SHaiyang Zhang 	if (ret < 0)
6381ce09e89SHaiyang Zhang 		goto cleanup;
6391ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6401ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
6411ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
6421ce09e89SHaiyang Zhang 	if (ret < 0)
6431ce09e89SHaiyang Zhang 		goto cleanup;
6441ce09e89SHaiyang Zhang 
6451ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6461ce09e89SHaiyang Zhang 	if (ret != 0)
6471ce09e89SHaiyang Zhang 		goto cleanup;
6481ce09e89SHaiyang Zhang 
6495362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
6505362855aSVitaly Kuznetsov 
6511ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
652867047c4Sstephen hemminger 	if (set_complete->status != RNDIS_STATUS_SUCCESS)
653867047c4Sstephen hemminger 		ret = -EIO;
6541ce09e89SHaiyang Zhang 
6551ce09e89SHaiyang Zhang cleanup:
6561ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6571ce09e89SHaiyang Zhang 	return ret;
6581ce09e89SHaiyang Zhang }
6591ce09e89SHaiyang Zhang 
660da19fcd0SLad, Prabhakar static int
661426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
6629749fed5Sstephen hemminger 				struct netvsc_device *nvdev,
6634a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6644a0e70aeSKY Srinivasan {
6654a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6664a0e70aeSKY Srinivasan 	struct rndis_request *request;
6674a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6684a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6694a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6704a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
671999028ccSNicholas Mc Guire 	int ret;
672af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
673af9893a3SKY Srinivasan 
674af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
675af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
676af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
677af9893a3SKY Srinivasan 		 * UDP checksum offload.
678af9893a3SKY Srinivasan 		 */
679af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
680af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
681af9893a3SKY Srinivasan 	}
6824a0e70aeSKY Srinivasan 
6834a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6844a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6854a0e70aeSKY Srinivasan 	if (!request)
6864a0e70aeSKY Srinivasan 		return -ENOMEM;
6874a0e70aeSKY Srinivasan 
6884a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6894a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6904a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6914a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6924a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
6934a0e70aeSKY Srinivasan 
6944a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
6954a0e70aeSKY Srinivasan 				set->info_buf_offset);
6964a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
6974a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
6984a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
6994a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
7004a0e70aeSKY Srinivasan 
7014a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
7024a0e70aeSKY Srinivasan 	if (ret != 0)
7034a0e70aeSKY Srinivasan 		goto cleanup;
7044a0e70aeSKY Srinivasan 
7055362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7064a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7074a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
708af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7094a0e70aeSKY Srinivasan 			   set_complete->status);
7104a0e70aeSKY Srinivasan 		ret = -EINVAL;
7114a0e70aeSKY Srinivasan 	}
7124a0e70aeSKY Srinivasan 
7134a0e70aeSKY Srinivasan cleanup:
7144a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7154a0e70aeSKY Srinivasan 	return ret;
7164a0e70aeSKY Srinivasan }
7171ce09e89SHaiyang Zhang 
718962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
719715e2ec5SHaiyang Zhang 			       const u8 *rss_key)
7205b54dac8SHaiyang Zhang {
7213d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7225b54dac8SHaiyang Zhang 	struct rndis_request *request;
7235b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7245b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7255b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
726962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7275b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7285b54dac8SHaiyang Zhang 	u32 *itab;
7295b54dac8SHaiyang Zhang 	u8 *keyp;
730999028ccSNicholas Mc Guire 	int i, ret;
7315b54dac8SHaiyang Zhang 
7325b54dac8SHaiyang Zhang 	request = get_rndis_request(
7335b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7345b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7355b54dac8SHaiyang Zhang 	if (!request)
7365b54dac8SHaiyang Zhang 		return -ENOMEM;
7375b54dac8SHaiyang Zhang 
7385b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7395b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7405b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7415b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7425b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7435b54dac8SHaiyang Zhang 
7445b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7455b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7465b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7475b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7485b54dac8SHaiyang Zhang 	rssp->flag = 0;
7495b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7504c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7514c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7525b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7535b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
754962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
755bfcbcb67SStephen Hemminger 	rssp->hashkey_offset = rssp->indirect_taboffset +
7565b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7575b54dac8SHaiyang Zhang 
7585b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7595b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7605b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
76147371300SHaiyang Zhang 		itab[i] = rdev->rx_table[i];
7625b54dac8SHaiyang Zhang 
7635b54dac8SHaiyang Zhang 	/* Set hask key values */
764bfcbcb67SStephen Hemminger 	keyp = (u8 *)((unsigned long)rssp + rssp->hashkey_offset);
765962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
7665b54dac8SHaiyang Zhang 
7675b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7685b54dac8SHaiyang Zhang 	if (ret != 0)
7695b54dac8SHaiyang Zhang 		goto cleanup;
7705b54dac8SHaiyang Zhang 
7715362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7725b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
773962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
774962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
775962f3feeSstephen hemminger 	else {
7765b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7775b54dac8SHaiyang Zhang 			   set_complete->status);
7785b54dac8SHaiyang Zhang 		ret = -EINVAL;
7795b54dac8SHaiyang Zhang 	}
7805b54dac8SHaiyang Zhang 
7815b54dac8SHaiyang Zhang cleanup:
7825b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7835b54dac8SHaiyang Zhang 	return ret;
7845b54dac8SHaiyang Zhang }
7855b54dac8SHaiyang Zhang 
786867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev,
787867047c4Sstephen hemminger 						 struct netvsc_device *net_device)
78895fa0405SHaiyang Zhang {
78995fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
79095fa0405SHaiyang Zhang 	u32 link_status;
79195fa0405SHaiyang Zhang 
792867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
79395fa0405SHaiyang Zhang 					 RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
79495fa0405SHaiyang Zhang 					 &link_status, &size);
79595fa0405SHaiyang Zhang }
79695fa0405SHaiyang Zhang 
797867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev,
798867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
799b37879e6SHaiyang Zhang {
800b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
801b37879e6SHaiyang Zhang 	u32 link_speed;
802b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
803b37879e6SHaiyang Zhang 	int ret;
804b37879e6SHaiyang Zhang 
805867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
806867047c4Sstephen hemminger 					RNDIS_OID_GEN_LINK_SPEED,
807b37879e6SHaiyang Zhang 					&link_speed, &size);
808b37879e6SHaiyang Zhang 
809b37879e6SHaiyang Zhang 	if (!ret) {
810b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
811b37879e6SHaiyang Zhang 
812b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
813b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
814b37879e6SHaiyang Zhang 		 */
815b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
816b37879e6SHaiyang Zhang 	}
817b37879e6SHaiyang Zhang 
818b37879e6SHaiyang Zhang 	return ret;
819b37879e6SHaiyang Zhang }
820b37879e6SHaiyang Zhang 
8214f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
8224f19c0d8Sstephen hemminger 					  u32 new_filter)
82395fa0405SHaiyang Zhang {
82495fa0405SHaiyang Zhang 	struct rndis_request *request;
82595fa0405SHaiyang Zhang 	struct rndis_set_request *set;
826999028ccSNicholas Mc Guire 	int ret;
82795fa0405SHaiyang Zhang 
8287eeb4a6eSStephen Hemminger 	if (dev->filter == new_filter)
8297eeb4a6eSStephen Hemminger 		return 0;
8307eeb4a6eSStephen Hemminger 
83151491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
83295fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
83395fa0405SHaiyang Zhang 			sizeof(u32));
834ce12b810Sstephen hemminger 	if (!request)
835ce12b810Sstephen hemminger 		return -ENOMEM;
836ce12b810Sstephen hemminger 
83795fa0405SHaiyang Zhang 	/* Setup the rndis set */
83895fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
83995fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
84095fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
84195fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
84295fa0405SHaiyang Zhang 
84395fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
84495fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
84595fa0405SHaiyang Zhang 
84695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
8477eeb4a6eSStephen Hemminger 	if (ret == 0) {
8485362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
8497eeb4a6eSStephen Hemminger 		dev->filter = new_filter;
8507eeb4a6eSStephen Hemminger 	}
85195fa0405SHaiyang Zhang 
85295fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
853ce12b810Sstephen hemminger 
85495fa0405SHaiyang Zhang 	return ret;
85595fa0405SHaiyang Zhang }
85695fa0405SHaiyang Zhang 
8574f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
8584f19c0d8Sstephen hemminger {
8594f19c0d8Sstephen hemminger 	struct rndis_device *rdev
8604f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
861009f766cSStephen Hemminger 	u32 filter = NDIS_PACKET_TYPE_DIRECTED;
862009f766cSStephen Hemminger 	unsigned int flags = rdev->ndev->flags;
8634f19c0d8Sstephen hemminger 
864009f766cSStephen Hemminger 	if (flags & IFF_PROMISC) {
865009f766cSStephen Hemminger 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
866009f766cSStephen Hemminger 	} else {
867f03dbb06SStephen Hemminger 		if (!netdev_mc_empty(rdev->ndev) || (flags & IFF_ALLMULTI))
868de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
869009f766cSStephen Hemminger 		if (flags & IFF_BROADCAST)
870de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_BROADCAST;
871009f766cSStephen Hemminger 	}
872009f766cSStephen Hemminger 
873009f766cSStephen Hemminger 	rndis_filter_set_packet_filter(rdev, filter);
8744f19c0d8Sstephen hemminger }
8754f19c0d8Sstephen hemminger 
8764f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
8774f19c0d8Sstephen hemminger {
8784f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
8794f19c0d8Sstephen hemminger 
8804f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
8814f19c0d8Sstephen hemminger }
8824f19c0d8Sstephen hemminger 
883867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev,
884867047c4Sstephen hemminger 				    struct netvsc_device *nvdev)
88595fa0405SHaiyang Zhang {
88695fa0405SHaiyang Zhang 	struct rndis_request *request;
88795fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
88895fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
88995fa0405SHaiyang Zhang 	u32 status;
890999028ccSNicholas Mc Guire 	int ret;
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 
93446b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
93546b4f7f5Sstephen hemminger {
93646b4f7f5Sstephen hemminger 	int i;
93746b4f7f5Sstephen hemminger 
93846b4f7f5Sstephen hemminger 	for (i = 0; i < nvdev->num_chn; i++) {
93946b4f7f5Sstephen hemminger 		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
94046b4f7f5Sstephen hemminger 
9417426b1a5Sstephen hemminger 		if (nvchan->mrc.first != nvchan->mrc.next)
9427426b1a5Sstephen hemminger 			return false;
9437426b1a5Sstephen hemminger 
94446b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
94546b4f7f5Sstephen hemminger 			return false;
94646b4f7f5Sstephen hemminger 	}
94746b4f7f5Sstephen hemminger 
94846b4f7f5Sstephen hemminger 	return true;
94946b4f7f5Sstephen hemminger }
95046b4f7f5Sstephen hemminger 
9510e96460eSStephen Hemminger static void rndis_filter_halt_device(struct netvsc_device *nvdev,
9520e96460eSStephen Hemminger 				     struct rndis_device *dev)
95395fa0405SHaiyang Zhang {
95495fa0405SHaiyang Zhang 	struct rndis_request *request;
95595fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
95695fa0405SHaiyang Zhang 
95795fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
95851491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
95995fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
96095fa0405SHaiyang Zhang 	if (!request)
96195fa0405SHaiyang Zhang 		goto cleanup;
96295fa0405SHaiyang Zhang 
96395fa0405SHaiyang Zhang 	/* Setup the rndis set */
96495fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
96595fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
96695fa0405SHaiyang Zhang 
96795fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
96895fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
96995fa0405SHaiyang Zhang 
97095fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
97195fa0405SHaiyang Zhang 
97295fa0405SHaiyang Zhang cleanup:
973ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
97400ecfb3bSstephen hemminger 
97500ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
97600ecfb3bSstephen hemminger 	wmb();
977ae9e63bbSHaiyang Zhang 
978ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
97946b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
980ae9e63bbSHaiyang Zhang 
98195fa0405SHaiyang Zhang 	if (request)
98295fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
98395fa0405SHaiyang Zhang }
98495fa0405SHaiyang Zhang 
98595fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
98695fa0405SHaiyang Zhang {
98795fa0405SHaiyang Zhang 	int ret;
98895fa0405SHaiyang Zhang 
98995fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
99095fa0405SHaiyang Zhang 		return 0;
99195fa0405SHaiyang Zhang 
99295fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
99395fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
99495fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
99595fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
99695fa0405SHaiyang Zhang 	if (ret == 0)
99795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
99895fa0405SHaiyang Zhang 
99995fa0405SHaiyang Zhang 	return ret;
100095fa0405SHaiyang Zhang }
100195fa0405SHaiyang Zhang 
100295fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
100395fa0405SHaiyang Zhang {
100495fa0405SHaiyang Zhang 	int ret;
100595fa0405SHaiyang Zhang 
100695fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
100795fa0405SHaiyang Zhang 		return 0;
100895fa0405SHaiyang Zhang 
10094f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
10104f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
10114f19c0d8Sstephen hemminger 
101295fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1013c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1014c3582a2cSHaiyang Zhang 		ret = 0;
1015c3582a2cSHaiyang Zhang 
101695fa0405SHaiyang Zhang 	if (ret == 0)
101795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
101895fa0405SHaiyang Zhang 
101995fa0405SHaiyang Zhang 	return ret;
102095fa0405SHaiyang Zhang }
102195fa0405SHaiyang Zhang 
10225b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
10235b54dac8SHaiyang Zhang {
10243d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
10253d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
1026867047c4Sstephen hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
1027867047c4Sstephen hemminger 	struct netvsc_device *nvscdev;
10285b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
10296de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
10306de38af6Sstephen hemminger 	int ret;
10315b54dac8SHaiyang Zhang 
1032867047c4Sstephen hemminger 	/* This is safe because this callback only happens when
1033867047c4Sstephen hemminger 	 * new device is being setup and waiting on the channel_init_wait.
1034867047c4Sstephen hemminger 	 */
1035867047c4Sstephen hemminger 	nvscdev = rcu_dereference_raw(ndev_ctx->nvdev);
1036867047c4Sstephen hemminger 	if (!nvscdev || chn_index >= nvscdev->num_chn)
10375b54dac8SHaiyang Zhang 		return;
10385b54dac8SHaiyang Zhang 
10396de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
10406de38af6Sstephen hemminger 
1041b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1042b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1043b1dd90ceSK. Y. Srinivasan 	 */
1044b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1045b1dd90ceSK. Y. Srinivasan 
1046bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1047bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
1048bffb1842SK. Y. Srinivasan 
1049a7f99d0fSStephen Hemminger 	ret = vmbus_open(new_sc, netvsc_ring_bytes,
1050a7f99d0fSStephen Hemminger 			 netvsc_ring_bytes, NULL, 0,
10516de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
105276bb5db5Sstephen hemminger 	if (ret == 0)
10536de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
105476bb5db5Sstephen hemminger 	else
10558195b139SStephen Hemminger 		netdev_notice(ndev, "sub channel open failed: %d\n", ret);
105615a863bfSstephen hemminger 
10578f2bb1deSStephen Hemminger 	if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn)
1058732e4985Sstephen hemminger 		wake_up(&nvscdev->subchan_open);
10595b54dac8SHaiyang Zhang }
10605b54dac8SHaiyang Zhang 
10618195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe.
10628195b139SStephen Hemminger  * This breaks overlap of processing the host message for the
10638195b139SStephen Hemminger  * new primary channel with the initialization of sub-channels.
10648195b139SStephen Hemminger  */
10658195b139SStephen Hemminger void rndis_set_subchannel(struct work_struct *w)
10668195b139SStephen Hemminger {
10678195b139SStephen Hemminger 	struct netvsc_device *nvdev
10688195b139SStephen Hemminger 		= container_of(w, struct netvsc_device, subchan_work);
10698195b139SStephen Hemminger 	struct nvsp_message *init_packet = &nvdev->channel_init_pkt;
10708195b139SStephen Hemminger 	struct net_device_context *ndev_ctx;
10718195b139SStephen Hemminger 	struct rndis_device *rdev;
10728195b139SStephen Hemminger 	struct net_device *ndev;
10738195b139SStephen Hemminger 	struct hv_device *hv_dev;
10748195b139SStephen Hemminger 	int i, ret;
10758195b139SStephen Hemminger 
10768195b139SStephen Hemminger 	if (!rtnl_trylock()) {
10778195b139SStephen Hemminger 		schedule_work(w);
10788195b139SStephen Hemminger 		return;
10798195b139SStephen Hemminger 	}
10808195b139SStephen Hemminger 
10818195b139SStephen Hemminger 	rdev = nvdev->extension;
10828195b139SStephen Hemminger 	if (!rdev)
10838195b139SStephen Hemminger 		goto unlock;	/* device was removed */
10848195b139SStephen Hemminger 
10858195b139SStephen Hemminger 	ndev = rdev->ndev;
10868195b139SStephen Hemminger 	ndev_ctx = netdev_priv(ndev);
10878195b139SStephen Hemminger 	hv_dev = ndev_ctx->device_ctx;
10888195b139SStephen Hemminger 
10898195b139SStephen Hemminger 	memset(init_packet, 0, sizeof(struct nvsp_message));
10908195b139SStephen Hemminger 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
10918195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
10928195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
10938195b139SStephen Hemminger 						nvdev->num_chn - 1;
1094ec966381SStephen Hemminger 	trace_nvsp_send(ndev, init_packet);
1095ec966381SStephen Hemminger 
10968195b139SStephen Hemminger 	ret = vmbus_sendpacket(hv_dev->channel, init_packet,
10978195b139SStephen Hemminger 			       sizeof(struct nvsp_message),
10988195b139SStephen Hemminger 			       (unsigned long)init_packet,
10998195b139SStephen Hemminger 			       VM_PKT_DATA_INBAND,
11008195b139SStephen Hemminger 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
11018195b139SStephen Hemminger 	if (ret) {
11028195b139SStephen Hemminger 		netdev_err(ndev, "sub channel allocate send failed: %d\n", ret);
11038195b139SStephen Hemminger 		goto failed;
11048195b139SStephen Hemminger 	}
11058195b139SStephen Hemminger 
11068195b139SStephen Hemminger 	wait_for_completion(&nvdev->channel_init_wait);
11078195b139SStephen Hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
11088195b139SStephen Hemminger 		netdev_err(ndev, "sub channel request failed\n");
11098195b139SStephen Hemminger 		goto failed;
11108195b139SStephen Hemminger 	}
11118195b139SStephen Hemminger 
11128195b139SStephen Hemminger 	nvdev->num_chn = 1 +
11138195b139SStephen Hemminger 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
11148195b139SStephen Hemminger 
11158195b139SStephen Hemminger 	/* wait for all sub channels to open */
11168195b139SStephen Hemminger 	wait_event(nvdev->subchan_open,
11178195b139SStephen Hemminger 		   atomic_read(&nvdev->open_chn) == nvdev->num_chn);
11188195b139SStephen Hemminger 
11198195b139SStephen Hemminger 	/* ignore failues from setting rss parameters, still have channels */
11208195b139SStephen Hemminger 	rndis_filter_set_rss_param(rdev, netvsc_hash_key);
11218195b139SStephen Hemminger 
11228195b139SStephen Hemminger 	netif_set_real_num_tx_queues(ndev, nvdev->num_chn);
11238195b139SStephen Hemminger 	netif_set_real_num_rx_queues(ndev, nvdev->num_chn);
11248195b139SStephen Hemminger 
1125a6fb6aa3SHaiyang Zhang 	for (i = 0; i < VRSS_SEND_TAB_SIZE; i++)
1126a6fb6aa3SHaiyang Zhang 		ndev_ctx->tx_table[i] = i % nvdev->num_chn;
1127a6fb6aa3SHaiyang Zhang 
11287b2ee50cSStephen Hemminger 	netif_device_attach(ndev);
11298195b139SStephen Hemminger 	rtnl_unlock();
11308195b139SStephen Hemminger 	return;
11318195b139SStephen Hemminger 
11328195b139SStephen Hemminger failed:
11338195b139SStephen Hemminger 	/* fallback to only primary channel */
11348195b139SStephen Hemminger 	for (i = 1; i < nvdev->num_chn; i++)
11358195b139SStephen Hemminger 		netif_napi_del(&nvdev->chan_table[i].napi);
11368195b139SStephen Hemminger 
11378195b139SStephen Hemminger 	nvdev->max_chn = 1;
11388195b139SStephen Hemminger 	nvdev->num_chn = 1;
11397b2ee50cSStephen Hemminger 
11407b2ee50cSStephen Hemminger 	netif_device_attach(ndev);
11418195b139SStephen Hemminger unlock:
11428195b139SStephen Hemminger 	rtnl_unlock();
11438195b139SStephen Hemminger }
11448195b139SStephen Hemminger 
1145aefd80e8SVitaly Kuznetsov static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device,
1146aefd80e8SVitaly Kuznetsov 				   struct netvsc_device *nvdev)
114795fa0405SHaiyang Zhang {
1148aefd80e8SVitaly Kuznetsov 	struct net_device *net = rndis_device->ndev;
11493d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
115023312a3bSstephen hemminger 	struct ndis_offload hwcaps;
11514a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
115223312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
1153aefd80e8SVitaly Kuznetsov 	int ret;
115495fa0405SHaiyang Zhang 
115523312a3bSstephen hemminger 	/* Find HW offload capabilities */
1156aefd80e8SVitaly Kuznetsov 	ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps);
11579749fed5Sstephen hemminger 	if (ret != 0)
1158aefd80e8SVitaly Kuznetsov 		return ret;
115923312a3bSstephen hemminger 
116023312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
11614a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
116223312a3bSstephen hemminger 
116323312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
116423312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
116523312a3bSstephen hemminger 
1166aefd80e8SVitaly Kuznetsov 	/* Reset previously set hw_features flags */
1167aefd80e8SVitaly Kuznetsov 	net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES;
1168aefd80e8SVitaly Kuznetsov 	net_device_ctx->tx_checksum_mask = 0;
1169aefd80e8SVitaly Kuznetsov 
117023312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
1171aefd80e8SVitaly Kuznetsov 	net->hw_features |= NETIF_F_RXCSUM;
117223312a3bSstephen hemminger 
117323312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
117423312a3bSstephen hemminger 		/* Can checksum TCP */
117523312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
117623312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
117723312a3bSstephen hemminger 
11784a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
117923312a3bSstephen hemminger 
118023312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
11814a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
118223312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
118323312a3bSstephen hemminger 
118423312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
118523312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
118623312a3bSstephen hemminger 		}
118723312a3bSstephen hemminger 
118823312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
118923312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
119023312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
119123312a3bSstephen hemminger 		}
119223312a3bSstephen hemminger 	}
119323312a3bSstephen hemminger 
119423312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
119523312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
119623312a3bSstephen hemminger 
119723312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
119823312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
119923312a3bSstephen hemminger 
120023312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
120123312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
120223312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
120323312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
120423312a3bSstephen hemminger 
120523312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
120623312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
120723312a3bSstephen hemminger 		}
120823312a3bSstephen hemminger 
120923312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
121023312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
121123312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
121223312a3bSstephen hemminger 		}
121323312a3bSstephen hemminger 	}
121423312a3bSstephen hemminger 
1215aefd80e8SVitaly Kuznetsov 	/* In case some hw_features disappeared we need to remove them from
1216aefd80e8SVitaly Kuznetsov 	 * net->features list as they're no longer supported.
1217aefd80e8SVitaly Kuznetsov 	 */
1218aefd80e8SVitaly Kuznetsov 	net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features;
1219aefd80e8SVitaly Kuznetsov 
122023312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
12214a0e70aeSKY Srinivasan 
1222aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, nvdev, &offloads);
1223aefd80e8SVitaly Kuznetsov 
1224aefd80e8SVitaly Kuznetsov 	return ret;
1225aefd80e8SVitaly Kuznetsov }
1226aefd80e8SVitaly Kuznetsov 
12270fe554a4SStephen Hemminger static void rndis_get_friendly_name(struct net_device *net,
12280fe554a4SStephen Hemminger 				    struct rndis_device *rndis_device,
12290fe554a4SStephen Hemminger 				    struct netvsc_device *net_device)
12300fe554a4SStephen Hemminger {
12310fe554a4SStephen Hemminger 	ucs2_char_t wname[256];
12320fe554a4SStephen Hemminger 	unsigned long len;
12330fe554a4SStephen Hemminger 	u8 ifalias[256];
12340fe554a4SStephen Hemminger 	u32 size;
12350fe554a4SStephen Hemminger 
12360fe554a4SStephen Hemminger 	size = sizeof(wname);
12370fe554a4SStephen Hemminger 	if (rndis_filter_query_device(rndis_device, net_device,
12380fe554a4SStephen Hemminger 				      RNDIS_OID_GEN_FRIENDLY_NAME,
12390fe554a4SStephen Hemminger 				      wname, &size) != 0)
1240d97cde6aSStephen Hemminger 		return;	/* ignore if host does not support */
1241d97cde6aSStephen Hemminger 
1242d97cde6aSStephen Hemminger 	if (size == 0)
1243d97cde6aSStephen Hemminger 		return;	/* name not set */
12440fe554a4SStephen Hemminger 
12450fe554a4SStephen Hemminger 	/* Convert Windows Unicode string to UTF-8 */
12460fe554a4SStephen Hemminger 	len = ucs2_as_utf8(ifalias, wname, sizeof(ifalias));
12470fe554a4SStephen Hemminger 
12480fe554a4SStephen Hemminger 	/* ignore the default value from host */
12490fe554a4SStephen Hemminger 	if (strcmp(ifalias, "Network Adapter") != 0)
12500fe554a4SStephen Hemminger 		dev_set_alias(net, ifalias, len);
12510fe554a4SStephen Hemminger }
12520fe554a4SStephen Hemminger 
1253aefd80e8SVitaly Kuznetsov struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
1254aefd80e8SVitaly Kuznetsov 				      struct netvsc_device_info *device_info)
1255aefd80e8SVitaly Kuznetsov {
1256aefd80e8SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
1257aefd80e8SVitaly Kuznetsov 	struct netvsc_device *net_device;
1258aefd80e8SVitaly Kuznetsov 	struct rndis_device *rndis_device;
1259aefd80e8SVitaly Kuznetsov 	struct ndis_recv_scale_cap rsscap;
1260aefd80e8SVitaly Kuznetsov 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
1261aefd80e8SVitaly Kuznetsov 	u32 mtu, size;
1262aefd80e8SVitaly Kuznetsov 	u32 num_possible_rss_qs;
1263aefd80e8SVitaly Kuznetsov 	int i, ret;
1264aefd80e8SVitaly Kuznetsov 
1265aefd80e8SVitaly Kuznetsov 	rndis_device = get_rndis_device();
1266aefd80e8SVitaly Kuznetsov 	if (!rndis_device)
1267aefd80e8SVitaly Kuznetsov 		return ERR_PTR(-ENODEV);
1268aefd80e8SVitaly Kuznetsov 
1269aefd80e8SVitaly Kuznetsov 	/* Let the inner driver handle this first to create the netvsc channel
1270aefd80e8SVitaly Kuznetsov 	 * NOTE! Once the channel is created, we may get a receive callback
1271aefd80e8SVitaly Kuznetsov 	 * (RndisFilterOnReceive()) before this call is completed
1272aefd80e8SVitaly Kuznetsov 	 */
1273aefd80e8SVitaly Kuznetsov 	net_device = netvsc_device_add(dev, device_info);
1274aefd80e8SVitaly Kuznetsov 	if (IS_ERR(net_device)) {
1275aefd80e8SVitaly Kuznetsov 		kfree(rndis_device);
1276aefd80e8SVitaly Kuznetsov 		return net_device;
1277aefd80e8SVitaly Kuznetsov 	}
1278aefd80e8SVitaly Kuznetsov 
1279aefd80e8SVitaly Kuznetsov 	/* Initialize the rndis device */
1280aefd80e8SVitaly Kuznetsov 	net_device->max_chn = 1;
1281aefd80e8SVitaly Kuznetsov 	net_device->num_chn = 1;
1282aefd80e8SVitaly Kuznetsov 
1283aefd80e8SVitaly Kuznetsov 	net_device->extension = rndis_device;
1284aefd80e8SVitaly Kuznetsov 	rndis_device->ndev = net;
1285aefd80e8SVitaly Kuznetsov 
1286aefd80e8SVitaly Kuznetsov 	/* Send the rndis initialization message */
1287aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_init_device(rndis_device, net_device);
1288aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1289aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1290aefd80e8SVitaly Kuznetsov 
1291aefd80e8SVitaly Kuznetsov 	/* Get the MTU from the host */
1292aefd80e8SVitaly Kuznetsov 	size = sizeof(u32);
1293aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device(rndis_device, net_device,
1294aefd80e8SVitaly Kuznetsov 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
1295aefd80e8SVitaly Kuznetsov 					&mtu, &size);
1296aefd80e8SVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
1297aefd80e8SVitaly Kuznetsov 		net->mtu = mtu;
1298aefd80e8SVitaly Kuznetsov 
1299aefd80e8SVitaly Kuznetsov 	/* Get the mac address */
1300aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device_mac(rndis_device, net_device);
1301aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1302aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1303aefd80e8SVitaly Kuznetsov 
1304aefd80e8SVitaly Kuznetsov 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
1305aefd80e8SVitaly Kuznetsov 
13060fe554a4SStephen Hemminger 	/* Get friendly name as ifalias*/
13070fe554a4SStephen Hemminger 	if (!net->ifalias)
13080fe554a4SStephen Hemminger 		rndis_get_friendly_name(net, rndis_device, net_device);
13090fe554a4SStephen Hemminger 
1310aefd80e8SVitaly Kuznetsov 	/* Query and set hardware capabilities */
1311aefd80e8SVitaly Kuznetsov 	ret = rndis_netdev_set_hwcaps(rndis_device, net_device);
1312aefd80e8SVitaly Kuznetsov 	if (ret != 0)
13134a0e70aeSKY Srinivasan 		goto err_dev_remv;
13144a0e70aeSKY Srinivasan 
1315867047c4Sstephen hemminger 	rndis_filter_query_device_link_status(rndis_device, net_device);
131695fa0405SHaiyang Zhang 
131793ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
131895fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1319dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
132095fa0405SHaiyang Zhang 
13215b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
132255be9f25SMohammed Gamal 		goto out;
13235b54dac8SHaiyang Zhang 
1324867047c4Sstephen hemminger 	rndis_filter_query_link_speed(rndis_device, net_device);
1325b37879e6SHaiyang Zhang 
13265b54dac8SHaiyang Zhang 	/* vRSS setup */
13275b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
1328867047c4Sstephen hemminger 	ret = rndis_filter_query_device(rndis_device, net_device,
13295b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
13305b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
13315b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
13325b54dac8SHaiyang Zhang 		goto out;
13335b54dac8SHaiyang Zhang 
133425a39f7fSHaiyang Zhang 	/* This guarantees that num_possible_rss_qs <= num_online_cpus */
133525a39f7fSHaiyang Zhang 	num_possible_rss_qs = min_t(u32, num_online_cpus(),
13363071ada4Sstephen hemminger 				    rsscap.num_recv_que);
13373071ada4Sstephen hemminger 
13383071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
13398ebdcc52SAndrew Schwartzmeyer 
13408ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
13413071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1342ff4a4419Sstephen hemminger 
1343ff4a4419Sstephen hemminger 	for (i = 0; i < ITAB_NUM; i++)
134447371300SHaiyang Zhang 		rndis_device->rx_table[i] = ethtool_rxfh_indir_default(
134547371300SHaiyang Zhang 						i, net_device->num_chn);
1346ff4a4419Sstephen hemminger 
1347732e4985Sstephen hemminger 	atomic_set(&net_device->open_chn, 1);
13488195b139SStephen Hemminger 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
13495b54dac8SHaiyang Zhang 
13507426b1a5Sstephen hemminger 	for (i = 1; i < net_device->num_chn; i++) {
13517426b1a5Sstephen hemminger 		ret = netvsc_alloc_recv_comp_ring(net_device, i);
13527426b1a5Sstephen hemminger 		if (ret) {
13537426b1a5Sstephen hemminger 			while (--i != 0)
13547426b1a5Sstephen hemminger 				vfree(net_device->chan_table[i].mrc.slots);
13557426b1a5Sstephen hemminger 			goto out;
13567426b1a5Sstephen hemminger 		}
13577426b1a5Sstephen hemminger 	}
13587426b1a5Sstephen hemminger 
13598195b139SStephen Hemminger 	for (i = 1; i < net_device->num_chn; i++)
13608195b139SStephen Hemminger 		netif_napi_add(net, &net_device->chan_table[i].napi,
13618195b139SStephen Hemminger 			       netvsc_poll, NAPI_POLL_WEIGHT);
13625b54dac8SHaiyang Zhang 
13638195b139SStephen Hemminger 	if (net_device->num_chn > 1)
13648195b139SStephen Hemminger 		schedule_work(&net_device->subchan_work);
13655362855aSVitaly Kuznetsov 
13665b54dac8SHaiyang Zhang out:
13678195b139SStephen Hemminger 	/* if unavailable, just proceed with one queue */
136859995370SAndrew Schwartzmeyer 	if (ret) {
136959995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
13705b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
137159995370SAndrew Schwartzmeyer 	}
1372b3e6b82aSKY Srinivasan 
13737b2ee50cSStephen Hemminger 	/* No sub channels, device is ready */
13747b2ee50cSStephen Hemminger 	if (net_device->num_chn == 1)
13757b2ee50cSStephen Hemminger 		netif_device_attach(net);
13767b2ee50cSStephen Hemminger 
13779749fed5Sstephen hemminger 	return net_device;
13784a0e70aeSKY Srinivasan 
13794a0e70aeSKY Srinivasan err_dev_remv:
13802289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
13819749fed5Sstephen hemminger 	return ERR_PTR(ret);
138295fa0405SHaiyang Zhang }
138395fa0405SHaiyang Zhang 
13842289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
13852289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
138695fa0405SHaiyang Zhang {
138795fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1388d66ab514SHaiyang Zhang 
138995fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
13900e96460eSStephen Hemminger 	rndis_filter_halt_device(net_dev, rndis_dev);
139195fa0405SHaiyang Zhang 
139295fa0405SHaiyang Zhang 	net_dev->extension = NULL;
139395fa0405SHaiyang Zhang 
139495fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
139595fa0405SHaiyang Zhang }
139695fa0405SHaiyang Zhang 
13972f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
139895fa0405SHaiyang Zhang {
13992f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
140095fa0405SHaiyang Zhang 		return -EINVAL;
140195fa0405SHaiyang Zhang 
14022f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
140395fa0405SHaiyang Zhang }
140495fa0405SHaiyang Zhang 
14052f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
140695fa0405SHaiyang Zhang {
14075fccab3bSHaiyang Zhang 	if (!nvdev)
140895fa0405SHaiyang Zhang 		return -EINVAL;
140995fa0405SHaiyang Zhang 
14105fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
141195fa0405SHaiyang Zhang }
1412