19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
295fa0405SHaiyang Zhang /*
395fa0405SHaiyang Zhang  * Copyright (c) 2009, Microsoft Corporation.
495fa0405SHaiyang Zhang  *
595fa0405SHaiyang Zhang  * Authors:
695fa0405SHaiyang Zhang  *   Haiyang Zhang <haiyangz@microsoft.com>
795fa0405SHaiyang Zhang  *   Hank Janssen  <hjanssen@microsoft.com>
895fa0405SHaiyang Zhang  */
9cc69837fSJakub Kicinski #include <linux/ethtool.h>
1095fa0405SHaiyang Zhang #include <linux/kernel.h>
1195fa0405SHaiyang Zhang #include <linux/sched.h>
1295fa0405SHaiyang Zhang #include <linux/wait.h>
1395fa0405SHaiyang Zhang #include <linux/highmem.h>
1495fa0405SHaiyang Zhang #include <linux/slab.h>
1595fa0405SHaiyang Zhang #include <linux/io.h>
1695fa0405SHaiyang Zhang #include <linux/if_ether.h>
1795fa0405SHaiyang Zhang #include <linux/netdevice.h>
181f5f3a75SHaiyang Zhang #include <linux/if_vlan.h>
191ce09e89SHaiyang Zhang #include <linux/nls.h>
20d6472302SStephen Rothwell #include <linux/vmalloc.h>
2127f5aa92Sstephen hemminger #include <linux/rtnetlink.h>
220fe554a4SStephen Hemminger #include <linux/ucs2_string.h>
23017e4254SCezar Bulinaru #include <linux/string.h>
24*4cab498fSShradha Gupta #include <linux/slab.h>
2595fa0405SHaiyang Zhang 
2695fa0405SHaiyang Zhang #include "hyperv_net.h"
27ec966381SStephen Hemminger #include "netvsc_trace.h"
2895fa0405SHaiyang Zhang 
294f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w);
3095fa0405SHaiyang Zhang 
3111d8620eSBoqun Feng #define RNDIS_EXT_LEN HV_HYP_PAGE_SIZE
3295fa0405SHaiyang Zhang struct rndis_request {
3395fa0405SHaiyang Zhang 	struct list_head list_ent;
3495fa0405SHaiyang Zhang 	struct completion  wait_event;
3595fa0405SHaiyang Zhang 
3695fa0405SHaiyang Zhang 	struct rndis_message response_msg;
37a3a6cab5SHaiyang Zhang 	/*
38a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
39a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
40a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
41a3a6cab5SHaiyang Zhang 	 * future.
42a3a6cab5SHaiyang Zhang 	 */
43a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
4495fa0405SHaiyang Zhang 
4595fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
4695fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
470f48917bSHaiyang Zhang 
4895fa0405SHaiyang Zhang 	struct rndis_message request_msg;
490f48917bSHaiyang Zhang 	/*
50a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
51a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
520f48917bSHaiyang Zhang 	 */
53a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
5495fa0405SHaiyang Zhang };
5595fa0405SHaiyang Zhang 
56962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
57962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
58962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
59962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
60962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
61962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
62962f3feeSstephen hemminger };
63962f3feeSstephen hemminger 
get_rndis_device(void)6495fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
6595fa0405SHaiyang Zhang {
6695fa0405SHaiyang Zhang 	struct rndis_device *device;
6795fa0405SHaiyang Zhang 
6895fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
6995fa0405SHaiyang Zhang 	if (!device)
7095fa0405SHaiyang Zhang 		return NULL;
7195fa0405SHaiyang Zhang 
7295fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
7395fa0405SHaiyang Zhang 
7495fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
754f19c0d8Sstephen hemminger 	INIT_WORK(&device->mcast_work, rndis_set_multicast);
7695fa0405SHaiyang Zhang 
7795fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
7895fa0405SHaiyang Zhang 
7995fa0405SHaiyang Zhang 	return device;
8095fa0405SHaiyang Zhang }
8195fa0405SHaiyang Zhang 
get_rndis_request(struct rndis_device * dev,u32 msg_type,u32 msg_len)8295fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
8395fa0405SHaiyang Zhang 					     u32 msg_type,
8495fa0405SHaiyang Zhang 					     u32 msg_len)
8595fa0405SHaiyang Zhang {
8695fa0405SHaiyang Zhang 	struct rndis_request *request;
8795fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
8895fa0405SHaiyang Zhang 	struct rndis_set_request *set;
8995fa0405SHaiyang Zhang 	unsigned long flags;
9095fa0405SHaiyang Zhang 
9195fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
9295fa0405SHaiyang Zhang 	if (!request)
9395fa0405SHaiyang Zhang 		return NULL;
9495fa0405SHaiyang Zhang 
9595fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
9695fa0405SHaiyang Zhang 
9795fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
9895fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
9995fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
10095fa0405SHaiyang Zhang 
1015b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1025b54dac8SHaiyang Zhang 
10395fa0405SHaiyang Zhang 	/*
10495fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
10595fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
10695fa0405SHaiyang Zhang 	 * template
10795fa0405SHaiyang Zhang 	 */
10895fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
10995fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
11095fa0405SHaiyang Zhang 
11195fa0405SHaiyang Zhang 	/* Add to the request list */
11295fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
11395fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
11495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
11595fa0405SHaiyang Zhang 
11695fa0405SHaiyang Zhang 	return request;
11795fa0405SHaiyang Zhang }
11895fa0405SHaiyang Zhang 
put_rndis_request(struct rndis_device * dev,struct rndis_request * req)11995fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
12095fa0405SHaiyang Zhang 			    struct rndis_request *req)
12195fa0405SHaiyang Zhang {
12295fa0405SHaiyang Zhang 	unsigned long flags;
12395fa0405SHaiyang Zhang 
12495fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12595fa0405SHaiyang Zhang 	list_del(&req->list_ent);
12695fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12795fa0405SHaiyang Zhang 
12895fa0405SHaiyang Zhang 	kfree(req);
12995fa0405SHaiyang Zhang }
13095fa0405SHaiyang Zhang 
dump_rndis_message(struct net_device * netdev,const struct rndis_message * rndis_msg,const void * data)13179cf1baeSStephen Hemminger static void dump_rndis_message(struct net_device *netdev,
1320ba35fe9SAndrea Parri (Microsoft) 			       const struct rndis_message *rndis_msg,
1330ba35fe9SAndrea Parri (Microsoft) 			       const void *data)
13495fa0405SHaiyang Zhang {
13595fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
13651491167SLinus Walleij 	case RNDIS_MSG_PACKET:
137505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= sizeof(struct rndis_packet)) {
1380ba35fe9SAndrea Parri (Microsoft) 			const struct rndis_packet *pkt = data + RNDIS_HEADER_SIZE;
13951491167SLinus Walleij 			netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14095fa0405SHaiyang Zhang 				   "data offset %u data len %u, # oob %u, "
14195fa0405SHaiyang Zhang 				   "oob offset %u, oob len %u, pkt offset %u, "
14295fa0405SHaiyang Zhang 				   "pkt len %u\n",
14395fa0405SHaiyang Zhang 				   rndis_msg->msg_len,
144505e3f00SAndrea Parri (Microsoft) 				   pkt->data_offset,
145505e3f00SAndrea Parri (Microsoft) 				   pkt->data_len,
146505e3f00SAndrea Parri (Microsoft) 				   pkt->num_oob_data_elements,
147505e3f00SAndrea Parri (Microsoft) 				   pkt->oob_data_offset,
148505e3f00SAndrea Parri (Microsoft) 				   pkt->oob_data_len,
149505e3f00SAndrea Parri (Microsoft) 				   pkt->per_pkt_info_offset,
150505e3f00SAndrea Parri (Microsoft) 				   pkt->per_pkt_info_len);
151505e3f00SAndrea Parri (Microsoft) 		}
15295fa0405SHaiyang Zhang 		break;
15395fa0405SHaiyang Zhang 
15451491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
155505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
156505e3f00SAndrea Parri (Microsoft) 				sizeof(struct rndis_initialize_complete)) {
157505e3f00SAndrea Parri (Microsoft) 			const struct rndis_initialize_complete *init_complete =
1580ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
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,
164505e3f00SAndrea Parri (Microsoft) 				init_complete->req_id,
165505e3f00SAndrea Parri (Microsoft) 				init_complete->status,
166505e3f00SAndrea Parri (Microsoft) 				init_complete->major_ver,
167505e3f00SAndrea Parri (Microsoft) 				init_complete->minor_ver,
168505e3f00SAndrea Parri (Microsoft) 				init_complete->dev_flags,
169505e3f00SAndrea Parri (Microsoft) 				init_complete->max_xfer_size,
170505e3f00SAndrea Parri (Microsoft) 				init_complete->max_pkt_per_msg,
171505e3f00SAndrea Parri (Microsoft) 				init_complete->pkt_alignment_factor);
172505e3f00SAndrea Parri (Microsoft) 		}
17395fa0405SHaiyang Zhang 		break;
17495fa0405SHaiyang Zhang 
17551491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
176505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
177505e3f00SAndrea Parri (Microsoft) 				sizeof(struct rndis_query_complete)) {
178505e3f00SAndrea Parri (Microsoft) 			const struct rndis_query_complete *query_complete =
1790ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
18051491167SLinus Walleij 			netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
18195fa0405SHaiyang Zhang 				"(len %u, id 0x%x, status 0x%x, buf len %u, "
18295fa0405SHaiyang Zhang 				"buf offset %u)\n",
18395fa0405SHaiyang Zhang 				rndis_msg->msg_len,
184505e3f00SAndrea Parri (Microsoft) 				query_complete->req_id,
185505e3f00SAndrea Parri (Microsoft) 				query_complete->status,
186505e3f00SAndrea Parri (Microsoft) 				query_complete->info_buflen,
187505e3f00SAndrea Parri (Microsoft) 				query_complete->info_buf_offset);
188505e3f00SAndrea Parri (Microsoft) 		}
18995fa0405SHaiyang Zhang 		break;
19095fa0405SHaiyang Zhang 
19151491167SLinus Walleij 	case RNDIS_MSG_SET_C:
192505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE + sizeof(struct rndis_set_complete)) {
193505e3f00SAndrea Parri (Microsoft) 			const struct rndis_set_complete *set_complete =
1940ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
19595fa0405SHaiyang Zhang 			netdev_dbg(netdev,
19651491167SLinus Walleij 				"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19795fa0405SHaiyang Zhang 				rndis_msg->msg_len,
198505e3f00SAndrea Parri (Microsoft) 				set_complete->req_id,
199505e3f00SAndrea Parri (Microsoft) 				set_complete->status);
200505e3f00SAndrea Parri (Microsoft) 		}
20195fa0405SHaiyang Zhang 		break;
20295fa0405SHaiyang Zhang 
20351491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
204505e3f00SAndrea Parri (Microsoft) 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
205505e3f00SAndrea Parri (Microsoft) 				sizeof(struct rndis_indicate_status)) {
206505e3f00SAndrea Parri (Microsoft) 			const struct rndis_indicate_status *indicate_status =
2070ba35fe9SAndrea Parri (Microsoft) 				data + RNDIS_HEADER_SIZE;
20851491167SLinus Walleij 			netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
20995fa0405SHaiyang Zhang 				"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
21095fa0405SHaiyang Zhang 				rndis_msg->msg_len,
211505e3f00SAndrea Parri (Microsoft) 				indicate_status->status,
212505e3f00SAndrea Parri (Microsoft) 				indicate_status->status_buflen,
213505e3f00SAndrea Parri (Microsoft) 				indicate_status->status_buf_offset);
214505e3f00SAndrea Parri (Microsoft) 		}
21595fa0405SHaiyang Zhang 		break;
21695fa0405SHaiyang Zhang 
21795fa0405SHaiyang Zhang 	default:
21895fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
21995fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
22095fa0405SHaiyang Zhang 			rndis_msg->msg_len);
22195fa0405SHaiyang Zhang 		break;
22295fa0405SHaiyang Zhang 	}
22395fa0405SHaiyang Zhang }
22495fa0405SHaiyang Zhang 
rndis_filter_send_request(struct rndis_device * dev,struct rndis_request * req)22595fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
22695fa0405SHaiyang Zhang 				  struct rndis_request *req)
22795fa0405SHaiyang Zhang {
22895fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
229b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
230a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
23102b6de01Sstephen hemminger 	int ret;
23295fa0405SHaiyang Zhang 
23395fa0405SHaiyang Zhang 	/* Setup the packet to send it */
23495fa0405SHaiyang Zhang 	packet = &req->pkt;
23595fa0405SHaiyang Zhang 
23695fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
23795fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
23895fa0405SHaiyang Zhang 
239a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
24011d8620eSBoqun Feng 					HV_HYP_PAGE_SHIFT;
241a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
24211d8620eSBoqun Feng 	pb[0].offset = offset_in_hvpage(&req->request_msg);
24395fa0405SHaiyang Zhang 
24499e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
24511d8620eSBoqun Feng 	if (pb[0].offset + pb[0].len > HV_HYP_PAGE_SIZE) {
24699e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
24711d8620eSBoqun Feng 		pb[0].len = HV_HYP_PAGE_SIZE -
248a9f2e2d6SKY Srinivasan 			pb[0].offset;
249a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
25011d8620eSBoqun Feng 			+ pb[0].len) >> HV_HYP_PAGE_SHIFT;
251a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
252a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
253a9f2e2d6SKY Srinivasan 			pb[0].len;
25499e3fcfaSHaiyang Zhang 	}
25599e3fcfaSHaiyang Zhang 
256ec966381SStephen Hemminger 	trace_rndis_send(dev->ndev, 0, &req->request_msg);
257ec966381SStephen Hemminger 
258867047c4Sstephen hemminger 	rcu_read_lock_bh();
259351e1581SHaiyang Zhang 	ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL, false);
260867047c4Sstephen hemminger 	rcu_read_unlock_bh();
261867047c4Sstephen hemminger 
26295fa0405SHaiyang Zhang 	return ret;
26395fa0405SHaiyang Zhang }
26495fa0405SHaiyang Zhang 
rndis_set_link_state(struct rndis_device * rdev,struct rndis_request * request)2651b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2661b07da51SHaiyang Zhang 				 struct rndis_request *request)
2671b07da51SHaiyang Zhang {
2681b07da51SHaiyang Zhang 	u32 link_status;
2691b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
270505e3f00SAndrea Parri (Microsoft) 	u32 msg_len = request->response_msg.msg_len;
271505e3f00SAndrea Parri (Microsoft) 
272505e3f00SAndrea Parri (Microsoft) 	/* Ensure the packet is big enough to access its fields */
273505e3f00SAndrea Parri (Microsoft) 	if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete))
274505e3f00SAndrea Parri (Microsoft) 		return;
2751b07da51SHaiyang Zhang 
2761b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2771b07da51SHaiyang Zhang 
2781b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
279505e3f00SAndrea Parri (Microsoft) 	    query_complete->info_buflen >= sizeof(u32) &&
280505e3f00SAndrea Parri (Microsoft) 	    query_complete->info_buf_offset >= sizeof(*query_complete) &&
281505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE >= query_complete->info_buf_offset &&
282505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset
283505e3f00SAndrea Parri (Microsoft) 			>= query_complete->info_buflen) {
2841b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2851b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2861b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2871b07da51SHaiyang Zhang 	}
2881b07da51SHaiyang Zhang }
2891b07da51SHaiyang Zhang 
rndis_filter_receive_response(struct net_device * ndev,struct netvsc_device * nvdev,struct rndis_message * resp,void * data)29002400fceSStephen Hemminger static void rndis_filter_receive_response(struct net_device *ndev,
29102400fceSStephen Hemminger 					  struct netvsc_device *nvdev,
2920ba35fe9SAndrea Parri (Microsoft) 					  struct rndis_message *resp,
2930ba35fe9SAndrea Parri (Microsoft) 					  void *data)
29495fa0405SHaiyang Zhang {
2950ba35fe9SAndrea Parri (Microsoft) 	u32 *req_id = &resp->msg.init_complete.req_id;
29602400fceSStephen Hemminger 	struct rndis_device *dev = nvdev->extension;
29795fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
29895fa0405SHaiyang Zhang 	bool found = false;
29995fa0405SHaiyang Zhang 	unsigned long flags;
30002400fceSStephen Hemminger 
30102400fceSStephen Hemminger 	/* This should never happen, it means control message
30202400fceSStephen Hemminger 	 * response received after device removed.
30302400fceSStephen Hemminger 	 */
30402400fceSStephen Hemminger 	if (dev->state == RNDIS_DEV_UNINITIALIZED) {
30502400fceSStephen Hemminger 		netdev_err(ndev,
30602400fceSStephen Hemminger 			   "got rndis message uninitialized\n");
30702400fceSStephen Hemminger 		return;
30802400fceSStephen Hemminger 	}
30995fa0405SHaiyang Zhang 
31044144185SAndres Beltran 	/* Ensure the packet is big enough to read req_id. Req_id is the 1st
31144144185SAndres Beltran 	 * field in any request/response message, so the payload should have at
31244144185SAndres Beltran 	 * least sizeof(u32) bytes
31344144185SAndres Beltran 	 */
31444144185SAndres Beltran 	if (resp->msg_len - RNDIS_HEADER_SIZE < sizeof(u32)) {
31544144185SAndres Beltran 		netdev_err(ndev, "rndis msg_len too small: %u\n",
31644144185SAndres Beltran 			   resp->msg_len);
31744144185SAndres Beltran 		return;
31844144185SAndres Beltran 	}
31944144185SAndres Beltran 
3200ba35fe9SAndrea Parri (Microsoft) 	/* Copy the request ID into nvchan->recv_buf */
3210ba35fe9SAndrea Parri (Microsoft) 	*req_id = *(u32 *)(data + RNDIS_HEADER_SIZE);
3220ba35fe9SAndrea Parri (Microsoft) 
32395fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
32495fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
32595fa0405SHaiyang Zhang 		/*
32695fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
32795fa0405SHaiyang Zhang 		 * field
32895fa0405SHaiyang Zhang 		 */
3290ba35fe9SAndrea Parri (Microsoft) 		if (request->request_msg.msg.init_req.req_id == *req_id) {
33095fa0405SHaiyang Zhang 			found = true;
33195fa0405SHaiyang Zhang 			break;
33295fa0405SHaiyang Zhang 		}
33395fa0405SHaiyang Zhang 	}
33495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
33595fa0405SHaiyang Zhang 
33695fa0405SHaiyang Zhang 	if (found) {
337a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
338a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
3390ba35fe9SAndrea Parri (Microsoft) 			memcpy(&request->response_msg, resp, RNDIS_HEADER_SIZE + sizeof(*req_id));
340017e4254SCezar Bulinaru 			unsafe_memcpy((void *)&request->response_msg + RNDIS_HEADER_SIZE + sizeof(*req_id),
3410ba35fe9SAndrea Parri (Microsoft) 			       data + RNDIS_HEADER_SIZE + sizeof(*req_id),
342017e4254SCezar Bulinaru 			       resp->msg_len - RNDIS_HEADER_SIZE - sizeof(*req_id),
343017e4254SCezar Bulinaru 			       "request->response_msg is followed by a padding of RNDIS_EXT_LEN inside rndis_request");
3441b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
3451b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
3461b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
3471b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
34895fa0405SHaiyang Zhang 		} else {
34995fa0405SHaiyang Zhang 			netdev_err(ndev,
35095fa0405SHaiyang Zhang 				"rndis response buffer overflow "
35195fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
35295fa0405SHaiyang Zhang 				resp->msg_len,
35386eedaccSKY Srinivasan 				sizeof(struct rndis_message));
35495fa0405SHaiyang Zhang 
35595fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
35651491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
35795fa0405SHaiyang Zhang 				/* does not have a request id field */
35895fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
359007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
36095fa0405SHaiyang Zhang 			} else {
36195fa0405SHaiyang Zhang 				request->response_msg.msg.
36295fa0405SHaiyang Zhang 				init_complete.status =
363007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
36495fa0405SHaiyang Zhang 			}
36595fa0405SHaiyang Zhang 		}
36695fa0405SHaiyang Zhang 
367846da38dSTianyu Lan 		netvsc_dma_unmap(((struct net_device_context *)
368846da38dSTianyu Lan 			netdev_priv(ndev))->device_ctx, &request->pkt);
36995fa0405SHaiyang Zhang 		complete(&request->wait_event);
37095fa0405SHaiyang Zhang 	} else {
37195fa0405SHaiyang Zhang 		netdev_err(ndev,
37295fa0405SHaiyang Zhang 			"no rndis request found for this response "
37395fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
3740ba35fe9SAndrea Parri (Microsoft) 			*req_id,
37595fa0405SHaiyang Zhang 			resp->ndis_msg_type);
37695fa0405SHaiyang Zhang 	}
37795fa0405SHaiyang Zhang }
37895fa0405SHaiyang Zhang 
3791f5f3a75SHaiyang Zhang /*
3801f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3811f5f3a75SHaiyang Zhang  * return NULL if not found.
3821f5f3a75SHaiyang Zhang  */
rndis_get_ppi(struct net_device * ndev,struct rndis_packet * rpkt,u32 rpkt_len,u32 type,u8 internal,u32 ppi_size,void * data)38344144185SAndres Beltran static inline void *rndis_get_ppi(struct net_device *ndev,
38444144185SAndres Beltran 				  struct rndis_packet *rpkt,
385505e3f00SAndrea Parri (Microsoft) 				  u32 rpkt_len, u32 type, u8 internal,
3860ba35fe9SAndrea Parri (Microsoft) 				  u32 ppi_size, void *data)
3871f5f3a75SHaiyang Zhang {
3881f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3891f5f3a75SHaiyang Zhang 	int len;
3901f5f3a75SHaiyang Zhang 
3911f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3921f5f3a75SHaiyang Zhang 		return NULL;
3931f5f3a75SHaiyang Zhang 
39444144185SAndres Beltran 	/* Validate info_offset and info_len */
39544144185SAndres Beltran 	if (rpkt->per_pkt_info_offset < sizeof(struct rndis_packet) ||
39644144185SAndres Beltran 	    rpkt->per_pkt_info_offset > rpkt_len) {
39744144185SAndres Beltran 		netdev_err(ndev, "Invalid per_pkt_info_offset: %u\n",
39844144185SAndres Beltran 			   rpkt->per_pkt_info_offset);
39944144185SAndres Beltran 		return NULL;
40044144185SAndres Beltran 	}
40144144185SAndres Beltran 
402505e3f00SAndrea Parri (Microsoft) 	if (rpkt->per_pkt_info_len < sizeof(*ppi) ||
403505e3f00SAndrea Parri (Microsoft) 	    rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) {
40444144185SAndres Beltran 		netdev_err(ndev, "Invalid per_pkt_info_len: %u\n",
40544144185SAndres Beltran 			   rpkt->per_pkt_info_len);
40644144185SAndres Beltran 		return NULL;
40744144185SAndres Beltran 	}
40844144185SAndres Beltran 
4091f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
4101f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
4110ba35fe9SAndrea Parri (Microsoft) 	/* Copy the PPIs into nvchan->recv_buf */
4120ba35fe9SAndrea Parri (Microsoft) 	memcpy(ppi, data + RNDIS_HEADER_SIZE + rpkt->per_pkt_info_offset, rpkt->per_pkt_info_len);
4131f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
4141f5f3a75SHaiyang Zhang 
4151f5f3a75SHaiyang Zhang 	while (len > 0) {
41644144185SAndres Beltran 		/* Validate ppi_offset and ppi_size */
41744144185SAndres Beltran 		if (ppi->size > len) {
41844144185SAndres Beltran 			netdev_err(ndev, "Invalid ppi size: %u\n", ppi->size);
41944144185SAndres Beltran 			continue;
42044144185SAndres Beltran 		}
42144144185SAndres Beltran 
42244144185SAndres Beltran 		if (ppi->ppi_offset >= ppi->size) {
42344144185SAndres Beltran 			netdev_err(ndev, "Invalid ppi_offset: %u\n", ppi->ppi_offset);
42444144185SAndres Beltran 			continue;
42544144185SAndres Beltran 		}
42644144185SAndres Beltran 
427505e3f00SAndrea Parri (Microsoft) 		if (ppi->type == type && ppi->internal == internal) {
428505e3f00SAndrea Parri (Microsoft) 			/* ppi->size should be big enough to hold the returned object. */
429505e3f00SAndrea Parri (Microsoft) 			if (ppi->size - ppi->ppi_offset < ppi_size) {
430505e3f00SAndrea Parri (Microsoft) 				netdev_err(ndev, "Invalid ppi: size %u ppi_offset %u\n",
431505e3f00SAndrea Parri (Microsoft) 					   ppi->size, ppi->ppi_offset);
432505e3f00SAndrea Parri (Microsoft) 				continue;
433505e3f00SAndrea Parri (Microsoft) 			}
4341f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
435505e3f00SAndrea Parri (Microsoft) 		}
4361f5f3a75SHaiyang Zhang 		len -= ppi->size;
4371f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
4381f5f3a75SHaiyang Zhang 	}
4391f5f3a75SHaiyang Zhang 
4401f5f3a75SHaiyang Zhang 	return NULL;
4411f5f3a75SHaiyang Zhang }
4421f5f3a75SHaiyang Zhang 
443c8e4eff4SHaiyang Zhang static inline
rsc_add_data(struct netvsc_channel * nvchan,const struct ndis_pkt_8021q_info * vlan,const struct ndis_tcp_ip_checksum_info * csum_info,const u32 * hash_info,void * data,u32 len)444c8e4eff4SHaiyang Zhang void rsc_add_data(struct netvsc_channel *nvchan,
445c8e4eff4SHaiyang Zhang 		  const struct ndis_pkt_8021q_info *vlan,
446c8e4eff4SHaiyang Zhang 		  const struct ndis_tcp_ip_checksum_info *csum_info,
4471fac7ca4SStephen Hemminger 		  const u32 *hash_info,
448c8e4eff4SHaiyang Zhang 		  void *data, u32 len)
449c8e4eff4SHaiyang Zhang {
450c8e4eff4SHaiyang Zhang 	u32 cnt = nvchan->rsc.cnt;
451c8e4eff4SHaiyang Zhang 
452c8e4eff4SHaiyang Zhang 	if (cnt) {
453c8e4eff4SHaiyang Zhang 		nvchan->rsc.pktlen += len;
454c8e4eff4SHaiyang Zhang 	} else {
4550ba35fe9SAndrea Parri (Microsoft) 		/* The data/values pointed by vlan, csum_info and hash_info are shared
4560ba35fe9SAndrea Parri (Microsoft) 		 * across the different 'fragments' of the RSC packet; store them into
4570ba35fe9SAndrea Parri (Microsoft) 		 * the packet itself.
4580ba35fe9SAndrea Parri (Microsoft) 		 */
4590ba35fe9SAndrea Parri (Microsoft) 		if (vlan != NULL) {
4600ba35fe9SAndrea Parri (Microsoft) 			memcpy(&nvchan->rsc.vlan, vlan, sizeof(*vlan));
4610ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags |= NVSC_RSC_VLAN;
4620ba35fe9SAndrea Parri (Microsoft) 		} else {
4630ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags &= ~NVSC_RSC_VLAN;
4640ba35fe9SAndrea Parri (Microsoft) 		}
4650ba35fe9SAndrea Parri (Microsoft) 		if (csum_info != NULL) {
4660ba35fe9SAndrea Parri (Microsoft) 			memcpy(&nvchan->rsc.csum_info, csum_info, sizeof(*csum_info));
4670ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags |= NVSC_RSC_CSUM_INFO;
4680ba35fe9SAndrea Parri (Microsoft) 		} else {
4690ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags &= ~NVSC_RSC_CSUM_INFO;
4700ba35fe9SAndrea Parri (Microsoft) 		}
471c8e4eff4SHaiyang Zhang 		nvchan->rsc.pktlen = len;
4720ba35fe9SAndrea Parri (Microsoft) 		if (hash_info != NULL) {
4738dff9808SAndrea Parri (Microsoft) 			nvchan->rsc.hash_info = *hash_info;
4740ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags |= NVSC_RSC_HASH_INFO;
4750ba35fe9SAndrea Parri (Microsoft) 		} else {
4760ba35fe9SAndrea Parri (Microsoft) 			nvchan->rsc.ppi_flags &= ~NVSC_RSC_HASH_INFO;
4770ba35fe9SAndrea Parri (Microsoft) 		}
478c8e4eff4SHaiyang Zhang 	}
479c8e4eff4SHaiyang Zhang 
480c8e4eff4SHaiyang Zhang 	nvchan->rsc.data[cnt] = data;
481c8e4eff4SHaiyang Zhang 	nvchan->rsc.len[cnt] = len;
482c8e4eff4SHaiyang Zhang 	nvchan->rsc.cnt++;
483c8e4eff4SHaiyang Zhang }
484c8e4eff4SHaiyang Zhang 
rndis_filter_receive_data(struct net_device * ndev,struct netvsc_device * nvdev,struct netvsc_channel * nvchan,struct rndis_message * msg,void * data,u32 data_buflen)485dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
486345ac089SStephen Hemminger 				     struct netvsc_device *nvdev,
487c8e4eff4SHaiyang Zhang 				     struct netvsc_channel *nvchan,
4883be9b5fdSHaiyang Zhang 				     struct rndis_message *msg,
4890ba35fe9SAndrea Parri (Microsoft) 				     void *data, u32 data_buflen)
49095fa0405SHaiyang Zhang {
491dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
492dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
493dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
494c8e4eff4SHaiyang Zhang 	const struct rndis_pktinfo_id *pktinfo_id;
4951fac7ca4SStephen Hemminger 	const u32 *hash_info;
49644144185SAndres Beltran 	u32 data_offset, rpkt_len;
497c8e4eff4SHaiyang Zhang 	bool rsc_more = false;
498c8e4eff4SHaiyang Zhang 	int ret;
49995fa0405SHaiyang Zhang 
50044144185SAndres Beltran 	/* Ensure data_buflen is big enough to read header fields */
50144144185SAndres Beltran 	if (data_buflen < RNDIS_HEADER_SIZE + sizeof(struct rndis_packet)) {
50244144185SAndres Beltran 		netdev_err(ndev, "invalid rndis pkt, data_buflen too small: %u\n",
50344144185SAndres Beltran 			   data_buflen);
50444144185SAndres Beltran 		return NVSP_STAT_FAIL;
50544144185SAndres Beltran 	}
50644144185SAndres Beltran 
5070ba35fe9SAndrea Parri (Microsoft) 	/* Copy the RNDIS packet into nvchan->recv_buf */
5080ba35fe9SAndrea Parri (Microsoft) 	memcpy(rndis_pkt, data + RNDIS_HEADER_SIZE, sizeof(*rndis_pkt));
5090ba35fe9SAndrea Parri (Microsoft) 
51044144185SAndres Beltran 	/* Validate rndis_pkt offset */
51144144185SAndres Beltran 	if (rndis_pkt->data_offset >= data_buflen - RNDIS_HEADER_SIZE) {
51244144185SAndres Beltran 		netdev_err(ndev, "invalid rndis packet offset: %u\n",
51344144185SAndres Beltran 			   rndis_pkt->data_offset);
51444144185SAndres Beltran 		return NVSP_STAT_FAIL;
51544144185SAndres Beltran 	}
51644144185SAndres Beltran 
51795fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
51895fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
51995fa0405SHaiyang Zhang 
52044144185SAndres Beltran 	rpkt_len = data_buflen - RNDIS_HEADER_SIZE;
521dc54a08cSstephen hemminger 	data_buflen -= data_offset;
5224b8a8bc9SWei Yongjun 
5234b8a8bc9SWei Yongjun 	/*
5244b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
5254b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
5264b8a8bc9SWei Yongjun 	 */
527dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
52802400fceSStephen Hemminger 		netdev_err(ndev, "rndis message buffer "
5294b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
5304b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
531dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
53210082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
5334b8a8bc9SWei Yongjun 	}
5344b8a8bc9SWei Yongjun 
5350ba35fe9SAndrea Parri (Microsoft) 	vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0, sizeof(*vlan),
5360ba35fe9SAndrea Parri (Microsoft) 			     data);
537dc54a08cSstephen hemminger 
538505e3f00SAndrea Parri (Microsoft) 	csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0,
5390ba35fe9SAndrea Parri (Microsoft) 				  sizeof(*csum_info), data);
540c8e4eff4SHaiyang Zhang 
541505e3f00SAndrea Parri (Microsoft) 	hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0,
5420ba35fe9SAndrea Parri (Microsoft) 				  sizeof(*hash_info), data);
5431fac7ca4SStephen Hemminger 
544505e3f00SAndrea Parri (Microsoft) 	pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1,
5450ba35fe9SAndrea Parri (Microsoft) 				   sizeof(*pktinfo_id), data);
5463be9b5fdSHaiyang Zhang 
547c8e4eff4SHaiyang Zhang 	/* Identify RSC frags, drop erroneous packets */
548c8e4eff4SHaiyang Zhang 	if (pktinfo_id && (pktinfo_id->flag & RNDIS_PKTINFO_SUBALLOC)) {
549c8e4eff4SHaiyang Zhang 		if (pktinfo_id->flag & RNDIS_PKTINFO_1ST_FRAG)
550c8e4eff4SHaiyang Zhang 			nvchan->rsc.cnt = 0;
551c8e4eff4SHaiyang Zhang 		else if (nvchan->rsc.cnt == 0)
552c8e4eff4SHaiyang Zhang 			goto drop;
553c8e4eff4SHaiyang Zhang 
554c8e4eff4SHaiyang Zhang 		rsc_more = true;
555c8e4eff4SHaiyang Zhang 
556c8e4eff4SHaiyang Zhang 		if (pktinfo_id->flag & RNDIS_PKTINFO_LAST_FRAG)
557c8e4eff4SHaiyang Zhang 			rsc_more = false;
558c8e4eff4SHaiyang Zhang 
559c8e4eff4SHaiyang Zhang 		if (rsc_more && nvchan->rsc.is_last)
560c8e4eff4SHaiyang Zhang 			goto drop;
561c8e4eff4SHaiyang Zhang 	} else {
562c8e4eff4SHaiyang Zhang 		nvchan->rsc.cnt = 0;
563c8e4eff4SHaiyang Zhang 	}
564c8e4eff4SHaiyang Zhang 
565c8e4eff4SHaiyang Zhang 	if (unlikely(nvchan->rsc.cnt >= NVSP_RSC_MAX))
566c8e4eff4SHaiyang Zhang 		goto drop;
567c8e4eff4SHaiyang Zhang 
568c8e4eff4SHaiyang Zhang 	/* Put data into per channel structure.
569c8e4eff4SHaiyang Zhang 	 * Also, remove the rndis trailer padding from rndis packet message
5704b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
5714b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
5724b8a8bc9SWei Yongjun 	 */
5731fac7ca4SStephen Hemminger 	rsc_add_data(nvchan, vlan, csum_info, hash_info,
5740ba35fe9SAndrea Parri (Microsoft) 		     data + data_offset, rndis_pkt->data_len);
575c8e4eff4SHaiyang Zhang 
576c8e4eff4SHaiyang Zhang 	if (rsc_more)
577c8e4eff4SHaiyang Zhang 		return NVSP_STAT_SUCCESS;
578c8e4eff4SHaiyang Zhang 
579c8e4eff4SHaiyang Zhang 	ret = netvsc_recv_callback(ndev, nvdev, nvchan);
580c8e4eff4SHaiyang Zhang 	nvchan->rsc.cnt = 0;
581c8e4eff4SHaiyang Zhang 
582c8e4eff4SHaiyang Zhang 	return ret;
583c8e4eff4SHaiyang Zhang 
584c8e4eff4SHaiyang Zhang drop:
585c8e4eff4SHaiyang Zhang 	return NVSP_STAT_FAIL;
58695fa0405SHaiyang Zhang }
58795fa0405SHaiyang Zhang 
rndis_filter_receive(struct net_device * ndev,struct netvsc_device * net_dev,struct netvsc_channel * nvchan,void * data,u32 buflen)588dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
589dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
590c8e4eff4SHaiyang Zhang 			 struct netvsc_channel *nvchan,
591dc54a08cSstephen hemminger 			 void *data, u32 buflen)
59295fa0405SHaiyang Zhang {
5933d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
5940ba35fe9SAndrea Parri (Microsoft) 	struct rndis_message *rndis_msg = nvchan->recv_buf;
5950ba35fe9SAndrea Parri (Microsoft) 
5960ba35fe9SAndrea Parri (Microsoft) 	if (buflen < RNDIS_HEADER_SIZE) {
5970ba35fe9SAndrea Parri (Microsoft) 		netdev_err(ndev, "Invalid rndis_msg (buflen: %u)\n", buflen);
5980ba35fe9SAndrea Parri (Microsoft) 		return NVSP_STAT_FAIL;
5990ba35fe9SAndrea Parri (Microsoft) 	}
6000ba35fe9SAndrea Parri (Microsoft) 
6010ba35fe9SAndrea Parri (Microsoft) 	/* Copy the RNDIS msg header into nvchan->recv_buf */
6020ba35fe9SAndrea Parri (Microsoft) 	memcpy(rndis_msg, data, RNDIS_HEADER_SIZE);
60395fa0405SHaiyang Zhang 
60444144185SAndres Beltran 	/* Validate incoming rndis_message packet */
6050ba35fe9SAndrea Parri (Microsoft) 	if (rndis_msg->msg_len < RNDIS_HEADER_SIZE ||
60644144185SAndres Beltran 	    buflen < rndis_msg->msg_len) {
60744144185SAndres Beltran 		netdev_err(ndev, "Invalid rndis_msg (buflen: %u, msg_len: %u)\n",
60844144185SAndres Beltran 			   buflen, rndis_msg->msg_len);
60944144185SAndres Beltran 		return NVSP_STAT_FAIL;
61044144185SAndres Beltran 	}
61144144185SAndres Beltran 
612505e3f00SAndrea Parri (Microsoft) 	if (netif_msg_rx_status(net_device_ctx))
6130ba35fe9SAndrea Parri (Microsoft) 		dump_rndis_message(ndev, rndis_msg, data);
614505e3f00SAndrea Parri (Microsoft) 
615ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
61651491167SLinus Walleij 	case RNDIS_MSG_PACKET:
617c8e4eff4SHaiyang Zhang 		return rndis_filter_receive_data(ndev, net_dev, nvchan,
6180ba35fe9SAndrea Parri (Microsoft) 						 rndis_msg, data, buflen);
61951491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
62051491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
62151491167SLinus Walleij 	case RNDIS_MSG_SET_C:
62295fa0405SHaiyang Zhang 		/* completion msgs */
6230ba35fe9SAndrea Parri (Microsoft) 		rndis_filter_receive_response(ndev, net_dev, rndis_msg, data);
62495fa0405SHaiyang Zhang 		break;
62595fa0405SHaiyang Zhang 
62651491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
62795fa0405SHaiyang Zhang 		/* notification msgs */
6283946688eSAndrea Parri (Microsoft) 		netvsc_linkstatus_callback(ndev, rndis_msg, data, buflen);
62995fa0405SHaiyang Zhang 		break;
63095fa0405SHaiyang Zhang 	default:
63195fa0405SHaiyang Zhang 		netdev_err(ndev,
63295fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
633ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
634ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
6355c71dadbSHaiyang Zhang 		return NVSP_STAT_FAIL;
63695fa0405SHaiyang Zhang 	}
63795fa0405SHaiyang Zhang 
6385c71dadbSHaiyang Zhang 	return NVSP_STAT_SUCCESS;
63995fa0405SHaiyang Zhang }
64095fa0405SHaiyang Zhang 
rndis_filter_query_device(struct rndis_device * dev,struct netvsc_device * nvdev,u32 oid,void * result,u32 * result_size)641867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev,
642867047c4Sstephen hemminger 				     struct netvsc_device *nvdev,
643867047c4Sstephen hemminger 				     u32 oid, void *result, u32 *result_size)
64495fa0405SHaiyang Zhang {
64595fa0405SHaiyang Zhang 	struct rndis_request *request;
64695fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
64795fa0405SHaiyang Zhang 	struct rndis_query_request *query;
64895fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
649505e3f00SAndrea Parri (Microsoft) 	u32 msg_len;
65095fa0405SHaiyang Zhang 	int ret = 0;
65195fa0405SHaiyang Zhang 
65295fa0405SHaiyang Zhang 	if (!result)
65395fa0405SHaiyang Zhang 		return -EINVAL;
65495fa0405SHaiyang Zhang 
65595fa0405SHaiyang Zhang 	*result_size = 0;
65651491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
65795fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
65895fa0405SHaiyang Zhang 	if (!request) {
65995fa0405SHaiyang Zhang 		ret = -ENOMEM;
66095fa0405SHaiyang Zhang 		goto cleanup;
66195fa0405SHaiyang Zhang 	}
66295fa0405SHaiyang Zhang 
66395fa0405SHaiyang Zhang 	/* Setup the rndis query */
66495fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
66595fa0405SHaiyang Zhang 	query->oid = oid;
66695fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
66795fa0405SHaiyang Zhang 	query->info_buflen = 0;
66895fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
66995fa0405SHaiyang Zhang 
67023312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
67123312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
67223312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
67323312a3bSstephen hemminger 		u8 ndis_rev;
67423312a3bSstephen hemminger 		size_t size;
67523312a3bSstephen hemminger 
67623312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
67723312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
67823312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
67923312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
68023312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
68123312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
68223312a3bSstephen hemminger 		} else {
68323312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
68423312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
68523312a3bSstephen hemminger 		}
68623312a3bSstephen hemminger 
68723312a3bSstephen hemminger 		request->request_msg.msg_len += size;
68823312a3bSstephen hemminger 		query->info_buflen = size;
68923312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
69023312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
69123312a3bSstephen hemminger 
69223312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
69323312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
69423312a3bSstephen hemminger 		hwcaps->header.size = size;
69523312a3bSstephen hemminger 
69623312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
6975b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
6985b54dac8SHaiyang Zhang 
6995b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
7005b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
7015b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
7025b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
7035b54dac8SHaiyang Zhang 						     query->info_buf_offset);
7045b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
7055b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
7065b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
7075b54dac8SHaiyang Zhang 	}
7085b54dac8SHaiyang Zhang 
70995fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
71095fa0405SHaiyang Zhang 	if (ret != 0)
71195fa0405SHaiyang Zhang 		goto cleanup;
71295fa0405SHaiyang Zhang 
7135362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
71495fa0405SHaiyang Zhang 
71595fa0405SHaiyang Zhang 	/* Copy the response back */
71695fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
717505e3f00SAndrea Parri (Microsoft) 	msg_len = request->response_msg.msg_len;
71895fa0405SHaiyang Zhang 
719505e3f00SAndrea Parri (Microsoft) 	/* Ensure the packet is big enough to access its fields */
720505e3f00SAndrea Parri (Microsoft) 	if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete)) {
721505e3f00SAndrea Parri (Microsoft) 		ret = -1;
722505e3f00SAndrea Parri (Microsoft) 		goto cleanup;
723505e3f00SAndrea Parri (Microsoft) 	}
724505e3f00SAndrea Parri (Microsoft) 
725505e3f00SAndrea Parri (Microsoft) 	if (query_complete->info_buflen > inresult_size ||
726505e3f00SAndrea Parri (Microsoft) 	    query_complete->info_buf_offset < sizeof(*query_complete) ||
727505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE < query_complete->info_buf_offset ||
728505e3f00SAndrea Parri (Microsoft) 	    msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset
729505e3f00SAndrea Parri (Microsoft) 			< query_complete->info_buflen) {
73095fa0405SHaiyang Zhang 		ret = -1;
73195fa0405SHaiyang Zhang 		goto cleanup;
73295fa0405SHaiyang Zhang 	}
73395fa0405SHaiyang Zhang 
73495fa0405SHaiyang Zhang 	memcpy(result,
73595fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
73695fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
73795fa0405SHaiyang Zhang 	       query_complete->info_buflen);
73895fa0405SHaiyang Zhang 
73995fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
74095fa0405SHaiyang Zhang 
74195fa0405SHaiyang Zhang cleanup:
74295fa0405SHaiyang Zhang 	if (request)
74395fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
74495fa0405SHaiyang Zhang 
74595fa0405SHaiyang Zhang 	return ret;
74695fa0405SHaiyang Zhang }
74795fa0405SHaiyang Zhang 
74823312a3bSstephen hemminger /* Get the hardware offload capabilities */
74923312a3bSstephen hemminger static int
rndis_query_hwcaps(struct rndis_device * dev,struct netvsc_device * net_device,struct ndis_offload * caps)750867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device,
751867047c4Sstephen hemminger 		   struct ndis_offload *caps)
75223312a3bSstephen hemminger {
75323312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
75423312a3bSstephen hemminger 	int ret;
75523312a3bSstephen hemminger 
75623312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
75723312a3bSstephen hemminger 
758867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
75923312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
76023312a3bSstephen hemminger 					caps, &caps_len);
76123312a3bSstephen hemminger 	if (ret)
76223312a3bSstephen hemminger 		return ret;
76323312a3bSstephen hemminger 
76423312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
76523312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
76623312a3bSstephen hemminger 			    caps->header.type);
76723312a3bSstephen hemminger 		return -EINVAL;
76823312a3bSstephen hemminger 	}
76923312a3bSstephen hemminger 
77023312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
77123312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
77223312a3bSstephen hemminger 			    caps->header.revision);
77323312a3bSstephen hemminger 		return -EINVAL;
77423312a3bSstephen hemminger 	}
77523312a3bSstephen hemminger 
77623312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
77723312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
77823312a3bSstephen hemminger 		netdev_warn(dev->ndev,
77923312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
78023312a3bSstephen hemminger 			    caps->header.size, caps_len);
78123312a3bSstephen hemminger 		return -EINVAL;
78223312a3bSstephen hemminger 	}
78323312a3bSstephen hemminger 
78423312a3bSstephen hemminger 	return 0;
78523312a3bSstephen hemminger }
78623312a3bSstephen hemminger 
rndis_filter_query_device_mac(struct rndis_device * dev,struct netvsc_device * net_device)787867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev,
788867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
78995fa0405SHaiyang Zhang {
79095fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
79195fa0405SHaiyang Zhang 
792867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
79395fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
79495fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
79595fa0405SHaiyang Zhang }
79695fa0405SHaiyang Zhang 
7971ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
7981ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
7991ce09e89SHaiyang Zhang 
rndis_filter_set_device_mac(struct netvsc_device * nvdev,const char * mac)800867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev,
801867047c4Sstephen hemminger 				const char *mac)
8021ce09e89SHaiyang Zhang {
8031ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
8041ce09e89SHaiyang Zhang 	struct rndis_request *request;
8051ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
8061ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
8071ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
8081ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
8091ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
8101ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
8111ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
812999028ccSNicholas Mc Guire 	int ret;
8131ce09e89SHaiyang Zhang 
8141ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
8151ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
8161ce09e89SHaiyang Zhang 	if (!request)
8171ce09e89SHaiyang Zhang 		return -ENOMEM;
8181ce09e89SHaiyang Zhang 
8191ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
8201ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
8211ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
8221ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
8231ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
8241ce09e89SHaiyang Zhang 
8251ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
8261ce09e89SHaiyang Zhang 		set->info_buf_offset);
8271ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
8281ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
8291ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
8301ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
8311ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
8321ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
8331ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
8341ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
8351ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
8361ce09e89SHaiyang Zhang 
8371ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
8381ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
8391ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
8401ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
8411ce09e89SHaiyang Zhang 	if (ret < 0)
8421ce09e89SHaiyang Zhang 		goto cleanup;
8431ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
8441ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
8451ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
8461ce09e89SHaiyang Zhang 	if (ret < 0)
8471ce09e89SHaiyang Zhang 		goto cleanup;
8481ce09e89SHaiyang Zhang 
8491ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
8501ce09e89SHaiyang Zhang 	if (ret != 0)
8511ce09e89SHaiyang Zhang 		goto cleanup;
8521ce09e89SHaiyang Zhang 
8535362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
8545362855aSVitaly Kuznetsov 
8551ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
856867047c4Sstephen hemminger 	if (set_complete->status != RNDIS_STATUS_SUCCESS)
857867047c4Sstephen hemminger 		ret = -EIO;
8581ce09e89SHaiyang Zhang 
8591ce09e89SHaiyang Zhang cleanup:
8601ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
8611ce09e89SHaiyang Zhang 	return ret;
8621ce09e89SHaiyang Zhang }
8631ce09e89SHaiyang Zhang 
864d6792a5aSHaiyang Zhang int
rndis_filter_set_offload_params(struct net_device * ndev,struct netvsc_device * nvdev,struct ndis_offload_params * req_offloads)865426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
8669749fed5Sstephen hemminger 				struct netvsc_device *nvdev,
8674a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
8684a0e70aeSKY Srinivasan {
8694a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
8704a0e70aeSKY Srinivasan 	struct rndis_request *request;
8714a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
8724a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
8734a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
8744a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
875999028ccSNicholas Mc Guire 	int ret;
876af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
877af9893a3SKY Srinivasan 
878af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
879af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
880af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
881af9893a3SKY Srinivasan 		 * UDP checksum offload.
882af9893a3SKY Srinivasan 		 */
883af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
884af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
885af9893a3SKY Srinivasan 	}
8864a0e70aeSKY Srinivasan 
8874a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
8884a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
8894a0e70aeSKY Srinivasan 	if (!request)
8904a0e70aeSKY Srinivasan 		return -ENOMEM;
8914a0e70aeSKY Srinivasan 
8924a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
8934a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
8944a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
8954a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
8964a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
8974a0e70aeSKY Srinivasan 
8984a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
8994a0e70aeSKY Srinivasan 				set->info_buf_offset);
9004a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
9014a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
9024a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
9034a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
9044a0e70aeSKY Srinivasan 
9054a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
9064a0e70aeSKY Srinivasan 	if (ret != 0)
9074a0e70aeSKY Srinivasan 		goto cleanup;
9084a0e70aeSKY Srinivasan 
9095362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
9104a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
9114a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
912af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
9134a0e70aeSKY Srinivasan 			   set_complete->status);
9144a0e70aeSKY Srinivasan 		ret = -EINVAL;
9154a0e70aeSKY Srinivasan 	}
9164a0e70aeSKY Srinivasan 
9174a0e70aeSKY Srinivasan cleanup:
9184a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
9194a0e70aeSKY Srinivasan 	return ret;
9204a0e70aeSKY Srinivasan }
9211ce09e89SHaiyang Zhang 
rndis_set_rss_param_msg(struct rndis_device * rdev,const u8 * rss_key,u16 flag)922b4a10c75SHaiyang Zhang static int rndis_set_rss_param_msg(struct rndis_device *rdev,
923b4a10c75SHaiyang Zhang 				   const u8 *rss_key, u16 flag)
9245b54dac8SHaiyang Zhang {
9253d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
926b0689faaSHaiyang Zhang 	struct net_device_context *ndc = netdev_priv(ndev);
9275b54dac8SHaiyang Zhang 	struct rndis_request *request;
9285b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
9295b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
9305b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
931*4cab498fSShradha Gupta 		     4 * ndc->rx_table_sz + NETVSC_HASH_KEYLEN;
9325b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
9335b54dac8SHaiyang Zhang 	u32 *itab;
9345b54dac8SHaiyang Zhang 	u8 *keyp;
935999028ccSNicholas Mc Guire 	int i, ret;
9365b54dac8SHaiyang Zhang 
9375b54dac8SHaiyang Zhang 	request = get_rndis_request(
9385b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
9395b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
9405b54dac8SHaiyang Zhang 	if (!request)
9415b54dac8SHaiyang Zhang 		return -ENOMEM;
9425b54dac8SHaiyang Zhang 
9435b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
9445b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
9455b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
9465b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
9475b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
9485b54dac8SHaiyang Zhang 
9495b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
9505b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
9515b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
9525b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
953b4a10c75SHaiyang Zhang 	rssp->flag = flag;
9545b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
9554c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
9564c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
957*4cab498fSShradha Gupta 	rssp->indirect_tabsize = 4 * ndc->rx_table_sz;
9585b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
959962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
960bfcbcb67SStephen Hemminger 	rssp->hashkey_offset = rssp->indirect_taboffset +
9615b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
9625b54dac8SHaiyang Zhang 
9635b54dac8SHaiyang Zhang 	/* Set indirection table entries */
9645b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
965*4cab498fSShradha Gupta 	for (i = 0; i < ndc->rx_table_sz; i++)
966b0689faaSHaiyang Zhang 		itab[i] = ndc->rx_table[i];
9675b54dac8SHaiyang Zhang 
9685b54dac8SHaiyang Zhang 	/* Set hask key values */
969bfcbcb67SStephen Hemminger 	keyp = (u8 *)((unsigned long)rssp + rssp->hashkey_offset);
970962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
9715b54dac8SHaiyang Zhang 
9725b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
9735b54dac8SHaiyang Zhang 	if (ret != 0)
9745b54dac8SHaiyang Zhang 		goto cleanup;
9755b54dac8SHaiyang Zhang 
9765362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
9775b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
978b4a10c75SHaiyang Zhang 	if (set_complete->status == RNDIS_STATUS_SUCCESS) {
979b4a10c75SHaiyang Zhang 		if (!(flag & NDIS_RSS_PARAM_FLAG_DISABLE_RSS) &&
980b4a10c75SHaiyang Zhang 		    !(flag & NDIS_RSS_PARAM_FLAG_HASH_KEY_UNCHANGED))
981962f3feeSstephen hemminger 			memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
982b4a10c75SHaiyang Zhang 
983b4a10c75SHaiyang Zhang 	} else {
9845b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
9855b54dac8SHaiyang Zhang 			   set_complete->status);
9865b54dac8SHaiyang Zhang 		ret = -EINVAL;
9875b54dac8SHaiyang Zhang 	}
9885b54dac8SHaiyang Zhang 
9895b54dac8SHaiyang Zhang cleanup:
9905b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
9915b54dac8SHaiyang Zhang 	return ret;
9925b54dac8SHaiyang Zhang }
9935b54dac8SHaiyang Zhang 
rndis_filter_set_rss_param(struct rndis_device * rdev,const u8 * rss_key)994b4a10c75SHaiyang Zhang int rndis_filter_set_rss_param(struct rndis_device *rdev,
995b4a10c75SHaiyang Zhang 			       const u8 *rss_key)
996b4a10c75SHaiyang Zhang {
997b4a10c75SHaiyang Zhang 	/* Disable RSS before change */
998b4a10c75SHaiyang Zhang 	rndis_set_rss_param_msg(rdev, rss_key,
999b4a10c75SHaiyang Zhang 				NDIS_RSS_PARAM_FLAG_DISABLE_RSS);
1000b4a10c75SHaiyang Zhang 
1001b4a10c75SHaiyang Zhang 	return rndis_set_rss_param_msg(rdev, rss_key, 0);
1002b4a10c75SHaiyang Zhang }
1003b4a10c75SHaiyang Zhang 
rndis_filter_query_device_link_status(struct rndis_device * dev,struct netvsc_device * net_device)1004867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev,
1005867047c4Sstephen hemminger 						 struct netvsc_device *net_device)
100695fa0405SHaiyang Zhang {
100795fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
100895fa0405SHaiyang Zhang 	u32 link_status;
100995fa0405SHaiyang Zhang 
1010867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
101195fa0405SHaiyang Zhang 					 RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
101295fa0405SHaiyang Zhang 					 &link_status, &size);
101395fa0405SHaiyang Zhang }
101495fa0405SHaiyang Zhang 
rndis_filter_query_link_speed(struct rndis_device * dev,struct netvsc_device * net_device)1015867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev,
1016867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
1017b37879e6SHaiyang Zhang {
1018b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
1019b37879e6SHaiyang Zhang 	u32 link_speed;
1020b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
1021b37879e6SHaiyang Zhang 	int ret;
1022b37879e6SHaiyang Zhang 
1023867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
1024867047c4Sstephen hemminger 					RNDIS_OID_GEN_LINK_SPEED,
1025b37879e6SHaiyang Zhang 					&link_speed, &size);
1026b37879e6SHaiyang Zhang 
1027b37879e6SHaiyang Zhang 	if (!ret) {
1028b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
1029b37879e6SHaiyang Zhang 
1030b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
1031b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
1032b37879e6SHaiyang Zhang 		 */
1033b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
1034b37879e6SHaiyang Zhang 	}
1035b37879e6SHaiyang Zhang 
1036b37879e6SHaiyang Zhang 	return ret;
1037b37879e6SHaiyang Zhang }
1038b37879e6SHaiyang Zhang 
rndis_filter_set_packet_filter(struct rndis_device * dev,u32 new_filter)10394f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
10404f19c0d8Sstephen hemminger 					  u32 new_filter)
104195fa0405SHaiyang Zhang {
104295fa0405SHaiyang Zhang 	struct rndis_request *request;
104395fa0405SHaiyang Zhang 	struct rndis_set_request *set;
1044999028ccSNicholas Mc Guire 	int ret;
104595fa0405SHaiyang Zhang 
10467eeb4a6eSStephen Hemminger 	if (dev->filter == new_filter)
10477eeb4a6eSStephen Hemminger 		return 0;
10487eeb4a6eSStephen Hemminger 
104951491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
105095fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
105195fa0405SHaiyang Zhang 			sizeof(u32));
1052ce12b810Sstephen hemminger 	if (!request)
1053ce12b810Sstephen hemminger 		return -ENOMEM;
1054ce12b810Sstephen hemminger 
105595fa0405SHaiyang Zhang 	/* Setup the rndis set */
105695fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
105795fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
105895fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
1059f2fcffe3SKees Cook 	set->info_buf_offset = offsetof(typeof(*set), info_buf);
1060f2fcffe3SKees Cook 	memcpy(set->info_buf, &new_filter, sizeof(u32));
106195fa0405SHaiyang Zhang 
106295fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
10637eeb4a6eSStephen Hemminger 	if (ret == 0) {
10645362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
10657eeb4a6eSStephen Hemminger 		dev->filter = new_filter;
10667eeb4a6eSStephen Hemminger 	}
106795fa0405SHaiyang Zhang 
106895fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
1069ce12b810Sstephen hemminger 
107095fa0405SHaiyang Zhang 	return ret;
107195fa0405SHaiyang Zhang }
107295fa0405SHaiyang Zhang 
rndis_set_multicast(struct work_struct * w)10734f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
10744f19c0d8Sstephen hemminger {
10754f19c0d8Sstephen hemminger 	struct rndis_device *rdev
10764f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
1077009f766cSStephen Hemminger 	u32 filter = NDIS_PACKET_TYPE_DIRECTED;
1078009f766cSStephen Hemminger 	unsigned int flags = rdev->ndev->flags;
10794f19c0d8Sstephen hemminger 
1080009f766cSStephen Hemminger 	if (flags & IFF_PROMISC) {
1081009f766cSStephen Hemminger 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
1082009f766cSStephen Hemminger 	} else {
1083f03dbb06SStephen Hemminger 		if (!netdev_mc_empty(rdev->ndev) || (flags & IFF_ALLMULTI))
1084de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
1085009f766cSStephen Hemminger 		if (flags & IFF_BROADCAST)
1086de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_BROADCAST;
1087009f766cSStephen Hemminger 	}
1088009f766cSStephen Hemminger 
1089009f766cSStephen Hemminger 	rndis_filter_set_packet_filter(rdev, filter);
10904f19c0d8Sstephen hemminger }
10914f19c0d8Sstephen hemminger 
rndis_filter_update(struct netvsc_device * nvdev)10924f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
10934f19c0d8Sstephen hemminger {
10944f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
10954f19c0d8Sstephen hemminger 
10964f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
10974f19c0d8Sstephen hemminger }
10984f19c0d8Sstephen hemminger 
rndis_filter_init_device(struct rndis_device * dev,struct netvsc_device * nvdev)1099867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev,
1100867047c4Sstephen hemminger 				    struct netvsc_device *nvdev)
110195fa0405SHaiyang Zhang {
110295fa0405SHaiyang Zhang 	struct rndis_request *request;
110395fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
110495fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
110595fa0405SHaiyang Zhang 	u32 status;
1106999028ccSNicholas Mc Guire 	int ret;
110795fa0405SHaiyang Zhang 
110851491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
110995fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
111095fa0405SHaiyang Zhang 	if (!request) {
111195fa0405SHaiyang Zhang 		ret = -ENOMEM;
111295fa0405SHaiyang Zhang 		goto cleanup;
111395fa0405SHaiyang Zhang 	}
111495fa0405SHaiyang Zhang 
111595fa0405SHaiyang Zhang 	/* Setup the rndis set */
111695fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
111795fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
111895fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
1119fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
112095fa0405SHaiyang Zhang 
112195fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
112295fa0405SHaiyang Zhang 
112395fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
112495fa0405SHaiyang Zhang 	if (ret != 0) {
112595fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
112695fa0405SHaiyang Zhang 		goto cleanup;
112795fa0405SHaiyang Zhang 	}
112895fa0405SHaiyang Zhang 
11295362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
113095fa0405SHaiyang Zhang 
113195fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
113295fa0405SHaiyang Zhang 	status = init_complete->status;
113395fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
113495fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
11357c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
11367c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
113795fa0405SHaiyang Zhang 		ret = 0;
113895fa0405SHaiyang Zhang 	} else {
113995fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
114095fa0405SHaiyang Zhang 		ret = -EINVAL;
114195fa0405SHaiyang Zhang 	}
114295fa0405SHaiyang Zhang 
114395fa0405SHaiyang Zhang cleanup:
114495fa0405SHaiyang Zhang 	if (request)
114595fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
114695fa0405SHaiyang Zhang 
114795fa0405SHaiyang Zhang 	return ret;
114895fa0405SHaiyang Zhang }
114995fa0405SHaiyang Zhang 
netvsc_device_idle(const struct netvsc_device * nvdev)115046b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
115146b4f7f5Sstephen hemminger {
115246b4f7f5Sstephen hemminger 	int i;
115346b4f7f5Sstephen hemminger 
115446b4f7f5Sstephen hemminger 	for (i = 0; i < nvdev->num_chn; i++) {
115546b4f7f5Sstephen hemminger 		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
115646b4f7f5Sstephen hemminger 
11577426b1a5Sstephen hemminger 		if (nvchan->mrc.first != nvchan->mrc.next)
11587426b1a5Sstephen hemminger 			return false;
11597426b1a5Sstephen hemminger 
116046b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
116146b4f7f5Sstephen hemminger 			return false;
116246b4f7f5Sstephen hemminger 	}
116346b4f7f5Sstephen hemminger 
116446b4f7f5Sstephen hemminger 	return true;
116546b4f7f5Sstephen hemminger }
116646b4f7f5Sstephen hemminger 
rndis_filter_halt_device(struct netvsc_device * nvdev,struct rndis_device * dev)11670e96460eSStephen Hemminger static void rndis_filter_halt_device(struct netvsc_device *nvdev,
11680e96460eSStephen Hemminger 				     struct rndis_device *dev)
116995fa0405SHaiyang Zhang {
117095fa0405SHaiyang Zhang 	struct rndis_request *request;
117195fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
117295fa0405SHaiyang Zhang 
117395fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
117451491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
117595fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
117695fa0405SHaiyang Zhang 	if (!request)
117795fa0405SHaiyang Zhang 		goto cleanup;
117895fa0405SHaiyang Zhang 
117995fa0405SHaiyang Zhang 	/* Setup the rndis set */
118095fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
118195fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
118295fa0405SHaiyang Zhang 
118395fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
118495fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
118595fa0405SHaiyang Zhang 
118695fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
118795fa0405SHaiyang Zhang 
118895fa0405SHaiyang Zhang cleanup:
1189ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
119000ecfb3bSstephen hemminger 
119100ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
119200ecfb3bSstephen hemminger 	wmb();
1193ae9e63bbSHaiyang Zhang 
1194ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
119546b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
1196ae9e63bbSHaiyang Zhang 
119795fa0405SHaiyang Zhang 	if (request)
119895fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
119995fa0405SHaiyang Zhang }
120095fa0405SHaiyang Zhang 
rndis_filter_open_device(struct rndis_device * dev)120195fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
120295fa0405SHaiyang Zhang {
120395fa0405SHaiyang Zhang 	int ret;
120495fa0405SHaiyang Zhang 
120595fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
120695fa0405SHaiyang Zhang 		return 0;
120795fa0405SHaiyang Zhang 
120895fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
120995fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
121095fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
121195fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
121295fa0405SHaiyang Zhang 	if (ret == 0)
121395fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
121495fa0405SHaiyang Zhang 
121595fa0405SHaiyang Zhang 	return ret;
121695fa0405SHaiyang Zhang }
121795fa0405SHaiyang Zhang 
rndis_filter_close_device(struct rndis_device * dev)121895fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
121995fa0405SHaiyang Zhang {
122095fa0405SHaiyang Zhang 	int ret;
122195fa0405SHaiyang Zhang 
122295fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
122395fa0405SHaiyang Zhang 		return 0;
122495fa0405SHaiyang Zhang 
12254f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
12264f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
12274f19c0d8Sstephen hemminger 
122895fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1229c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1230c3582a2cSHaiyang Zhang 		ret = 0;
1231c3582a2cSHaiyang Zhang 
123295fa0405SHaiyang Zhang 	if (ret == 0)
123395fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
123495fa0405SHaiyang Zhang 
123595fa0405SHaiyang Zhang 	return ret;
123695fa0405SHaiyang Zhang }
123795fa0405SHaiyang Zhang 
netvsc_sc_open(struct vmbus_channel * new_sc)12385b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
12395b54dac8SHaiyang Zhang {
12403d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
12413d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
1242867047c4Sstephen hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
1243867047c4Sstephen hemminger 	struct netvsc_device *nvscdev;
12445b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
12456de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
12466de38af6Sstephen hemminger 	int ret;
12475b54dac8SHaiyang Zhang 
1248867047c4Sstephen hemminger 	/* This is safe because this callback only happens when
1249867047c4Sstephen hemminger 	 * new device is being setup and waiting on the channel_init_wait.
1250867047c4Sstephen hemminger 	 */
1251867047c4Sstephen hemminger 	nvscdev = rcu_dereference_raw(ndev_ctx->nvdev);
1252867047c4Sstephen hemminger 	if (!nvscdev || chn_index >= nvscdev->num_chn)
12535b54dac8SHaiyang Zhang 		return;
12545b54dac8SHaiyang Zhang 
12556de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
12566de38af6Sstephen hemminger 
1257b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1258b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1259b1dd90ceSK. Y. Srinivasan 	 */
1260b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1261b1dd90ceSK. Y. Srinivasan 
1262bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1263bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
1264bffb1842SK. Y. Srinivasan 
1265bf5fd8caSAndrea Parri (Microsoft) 	new_sc->next_request_id_callback = vmbus_next_request_id;
1266bf5fd8caSAndrea Parri (Microsoft) 	new_sc->request_addr_callback = vmbus_request_addr;
12674d18fcc9SAndres Beltran 	new_sc->rqstor_size = netvsc_rqstor_size(netvsc_ring_bytes);
1268adae1e93SAndres Beltran 	new_sc->max_pkt_size = NETVSC_MAX_PKT_SIZE;
1269adae1e93SAndres Beltran 
1270a7f99d0fSStephen Hemminger 	ret = vmbus_open(new_sc, netvsc_ring_bytes,
1271a7f99d0fSStephen Hemminger 			 netvsc_ring_bytes, NULL, 0,
12726de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
127376bb5db5Sstephen hemminger 	if (ret == 0)
12746de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
127576bb5db5Sstephen hemminger 	else
12768195b139SStephen Hemminger 		netdev_notice(ndev, "sub channel open failed: %d\n", ret);
127715a863bfSstephen hemminger 
12788f2bb1deSStephen Hemminger 	if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn)
1279732e4985Sstephen hemminger 		wake_up(&nvscdev->subchan_open);
12805b54dac8SHaiyang Zhang }
12815b54dac8SHaiyang Zhang 
12828195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe.
12838195b139SStephen Hemminger  * This breaks overlap of processing the host message for the
12848195b139SStephen Hemminger  * new primary channel with the initialization of sub-channels.
12858195b139SStephen Hemminger  */
rndis_set_subchannel(struct net_device * ndev,struct netvsc_device * nvdev,struct netvsc_device_info * dev_info)128617d91256SHaiyang Zhang int rndis_set_subchannel(struct net_device *ndev,
128717d91256SHaiyang Zhang 			 struct netvsc_device *nvdev,
128817d91256SHaiyang Zhang 			 struct netvsc_device_info *dev_info)
12898195b139SStephen Hemminger {
12908195b139SStephen Hemminger 	struct nvsp_message *init_packet = &nvdev->channel_init_pkt;
12913ffe64f1SStephen Hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
12923ffe64f1SStephen Hemminger 	struct hv_device *hv_dev = ndev_ctx->device_ctx;
12933ffe64f1SStephen Hemminger 	struct rndis_device *rdev = nvdev->extension;
12948195b139SStephen Hemminger 	int i, ret;
12958195b139SStephen Hemminger 
12963ffe64f1SStephen Hemminger 	ASSERT_RTNL();
12978195b139SStephen Hemminger 
12988195b139SStephen Hemminger 	memset(init_packet, 0, sizeof(struct nvsp_message));
12998195b139SStephen Hemminger 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
13008195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
13018195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
13028195b139SStephen Hemminger 						nvdev->num_chn - 1;
1303ec966381SStephen Hemminger 	trace_nvsp_send(ndev, init_packet);
1304ec966381SStephen Hemminger 
13058195b139SStephen Hemminger 	ret = vmbus_sendpacket(hv_dev->channel, init_packet,
13068195b139SStephen Hemminger 			       sizeof(struct nvsp_message),
13078195b139SStephen Hemminger 			       (unsigned long)init_packet,
13088195b139SStephen Hemminger 			       VM_PKT_DATA_INBAND,
13098195b139SStephen Hemminger 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
13108195b139SStephen Hemminger 	if (ret) {
13118195b139SStephen Hemminger 		netdev_err(ndev, "sub channel allocate send failed: %d\n", ret);
13123ffe64f1SStephen Hemminger 		return ret;
13138195b139SStephen Hemminger 	}
13148195b139SStephen Hemminger 
13158195b139SStephen Hemminger 	wait_for_completion(&nvdev->channel_init_wait);
13168195b139SStephen Hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
13178195b139SStephen Hemminger 		netdev_err(ndev, "sub channel request failed\n");
13183ffe64f1SStephen Hemminger 		return -EIO;
13198195b139SStephen Hemminger 	}
13208195b139SStephen Hemminger 
1321206ad34dSAndrea Parri (Microsoft) 	/* Check that number of allocated sub channel is within the expected range */
1322206ad34dSAndrea Parri (Microsoft) 	if (init_packet->msg.v5_msg.subchn_comp.num_subchannels > nvdev->num_chn - 1) {
1323206ad34dSAndrea Parri (Microsoft) 		netdev_err(ndev, "invalid number of allocated sub channel\n");
1324206ad34dSAndrea Parri (Microsoft) 		return -EINVAL;
1325206ad34dSAndrea Parri (Microsoft) 	}
13268195b139SStephen Hemminger 	nvdev->num_chn = 1 +
13278195b139SStephen Hemminger 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
13288195b139SStephen Hemminger 
13298195b139SStephen Hemminger 	/* wait for all sub channels to open */
13308195b139SStephen Hemminger 	wait_event(nvdev->subchan_open,
13318195b139SStephen Hemminger 		   atomic_read(&nvdev->open_chn) == nvdev->num_chn);
13328195b139SStephen Hemminger 
1333c39ea5cbSHaiyang Zhang 	for (i = 0; i < VRSS_SEND_TAB_SIZE; i++)
1334c39ea5cbSHaiyang Zhang 		ndev_ctx->tx_table[i] = i % nvdev->num_chn;
1335c39ea5cbSHaiyang Zhang 
133652d3b494SAdrian Vladu 	/* ignore failures from setting rss parameters, still have channels */
133717d91256SHaiyang Zhang 	if (dev_info)
133817d91256SHaiyang Zhang 		rndis_filter_set_rss_param(rdev, dev_info->rss_key);
133917d91256SHaiyang Zhang 	else
13408195b139SStephen Hemminger 		rndis_filter_set_rss_param(rdev, netvsc_hash_key);
13418195b139SStephen Hemminger 
13428195b139SStephen Hemminger 	netif_set_real_num_tx_queues(ndev, nvdev->num_chn);
13438195b139SStephen Hemminger 	netif_set_real_num_rx_queues(ndev, nvdev->num_chn);
13448195b139SStephen Hemminger 
13453ffe64f1SStephen Hemminger 	return 0;
13468195b139SStephen Hemminger }
13478195b139SStephen Hemminger 
rndis_netdev_set_hwcaps(struct rndis_device * rndis_device,struct netvsc_device * nvdev)1348aefd80e8SVitaly Kuznetsov static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device,
1349aefd80e8SVitaly Kuznetsov 				   struct netvsc_device *nvdev)
135095fa0405SHaiyang Zhang {
1351aefd80e8SVitaly Kuznetsov 	struct net_device *net = rndis_device->ndev;
13523d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
135323312a3bSstephen hemminger 	struct ndis_offload hwcaps;
13544a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
13557c4e983cSAlexander Duyck 	unsigned int gso_max_size = GSO_LEGACY_MAX_SIZE;
1356aefd80e8SVitaly Kuznetsov 	int ret;
135795fa0405SHaiyang Zhang 
135823312a3bSstephen hemminger 	/* Find HW offload capabilities */
1359aefd80e8SVitaly Kuznetsov 	ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps);
13609749fed5Sstephen hemminger 	if (ret != 0)
1361aefd80e8SVitaly Kuznetsov 		return ret;
136223312a3bSstephen hemminger 
136323312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
13644a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
136523312a3bSstephen hemminger 
136623312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
136723312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
136823312a3bSstephen hemminger 
1369aefd80e8SVitaly Kuznetsov 	/* Reset previously set hw_features flags */
1370aefd80e8SVitaly Kuznetsov 	net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES;
1371aefd80e8SVitaly Kuznetsov 	net_device_ctx->tx_checksum_mask = 0;
1372aefd80e8SVitaly Kuznetsov 
137323312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
1374aefd80e8SVitaly Kuznetsov 	net->hw_features |= NETIF_F_RXCSUM;
1375b441f795SHaiyang Zhang 	net->hw_features |= NETIF_F_SG;
13766f3aeb1bSStephen Hemminger 	net->hw_features |= NETIF_F_RXHASH;
137723312a3bSstephen hemminger 
137823312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
137923312a3bSstephen hemminger 		/* Can checksum TCP */
138023312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
138123312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
138223312a3bSstephen hemminger 
13834a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
138423312a3bSstephen hemminger 
138523312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
13864a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
138723312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
138823312a3bSstephen hemminger 
138923312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
139023312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
139123312a3bSstephen hemminger 		}
139223312a3bSstephen hemminger 
139323312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
139423312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
139523312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
139623312a3bSstephen hemminger 		}
139723312a3bSstephen hemminger 	}
139823312a3bSstephen hemminger 
139923312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
140023312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
140123312a3bSstephen hemminger 
140223312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
140323312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
140423312a3bSstephen hemminger 
140523312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
140623312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
140723312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
140823312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
140923312a3bSstephen hemminger 
141023312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
141123312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
141223312a3bSstephen hemminger 		}
141323312a3bSstephen hemminger 
141423312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
141523312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
141623312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
141723312a3bSstephen hemminger 		}
141823312a3bSstephen hemminger 	}
141923312a3bSstephen hemminger 
1420c8e4eff4SHaiyang Zhang 	if (hwcaps.rsc.ip4 && hwcaps.rsc.ip6) {
1421c8e4eff4SHaiyang Zhang 		net->hw_features |= NETIF_F_LRO;
1422c8e4eff4SHaiyang Zhang 
1423d6792a5aSHaiyang Zhang 		if (net->features & NETIF_F_LRO) {
1424c8e4eff4SHaiyang Zhang 			offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
1425c8e4eff4SHaiyang Zhang 			offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
1426d6792a5aSHaiyang Zhang 		} else {
1427d6792a5aSHaiyang Zhang 			offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
1428d6792a5aSHaiyang Zhang 			offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
1429d6792a5aSHaiyang Zhang 		}
1430c8e4eff4SHaiyang Zhang 	}
1431c8e4eff4SHaiyang Zhang 
1432aefd80e8SVitaly Kuznetsov 	/* In case some hw_features disappeared we need to remove them from
1433aefd80e8SVitaly Kuznetsov 	 * net->features list as they're no longer supported.
1434aefd80e8SVitaly Kuznetsov 	 */
1435aefd80e8SVitaly Kuznetsov 	net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features;
1436aefd80e8SVitaly Kuznetsov 
1437ee8b7a11SJakub Kicinski 	netif_set_tso_max_size(net, gso_max_size);
14384a0e70aeSKY Srinivasan 
1439aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, nvdev, &offloads);
1440aefd80e8SVitaly Kuznetsov 
1441aefd80e8SVitaly Kuznetsov 	return ret;
1442aefd80e8SVitaly Kuznetsov }
1443aefd80e8SVitaly Kuznetsov 
rndis_get_friendly_name(struct net_device * net,struct rndis_device * rndis_device,struct netvsc_device * net_device)14440fe554a4SStephen Hemminger static void rndis_get_friendly_name(struct net_device *net,
14450fe554a4SStephen Hemminger 				    struct rndis_device *rndis_device,
14460fe554a4SStephen Hemminger 				    struct netvsc_device *net_device)
14470fe554a4SStephen Hemminger {
14480fe554a4SStephen Hemminger 	ucs2_char_t wname[256];
14490fe554a4SStephen Hemminger 	unsigned long len;
14500fe554a4SStephen Hemminger 	u8 ifalias[256];
14510fe554a4SStephen Hemminger 	u32 size;
14520fe554a4SStephen Hemminger 
14530fe554a4SStephen Hemminger 	size = sizeof(wname);
14540fe554a4SStephen Hemminger 	if (rndis_filter_query_device(rndis_device, net_device,
14550fe554a4SStephen Hemminger 				      RNDIS_OID_GEN_FRIENDLY_NAME,
14560fe554a4SStephen Hemminger 				      wname, &size) != 0)
1457d97cde6aSStephen Hemminger 		return;	/* ignore if host does not support */
1458d97cde6aSStephen Hemminger 
1459d97cde6aSStephen Hemminger 	if (size == 0)
1460d97cde6aSStephen Hemminger 		return;	/* name not set */
14610fe554a4SStephen Hemminger 
14620fe554a4SStephen Hemminger 	/* Convert Windows Unicode string to UTF-8 */
14630fe554a4SStephen Hemminger 	len = ucs2_as_utf8(ifalias, wname, sizeof(ifalias));
14640fe554a4SStephen Hemminger 
14650fe554a4SStephen Hemminger 	/* ignore the default value from host */
14660fe554a4SStephen Hemminger 	if (strcmp(ifalias, "Network Adapter") != 0)
14670fe554a4SStephen Hemminger 		dev_set_alias(net, ifalias, len);
14680fe554a4SStephen Hemminger }
14690fe554a4SStephen Hemminger 
rndis_filter_device_add(struct hv_device * dev,struct netvsc_device_info * device_info)1470aefd80e8SVitaly Kuznetsov struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
1471aefd80e8SVitaly Kuznetsov 				      struct netvsc_device_info *device_info)
1472aefd80e8SVitaly Kuznetsov {
1473aefd80e8SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
1474b0689faaSHaiyang Zhang 	struct net_device_context *ndc = netdev_priv(net);
1475aefd80e8SVitaly Kuznetsov 	struct netvsc_device *net_device;
1476aefd80e8SVitaly Kuznetsov 	struct rndis_device *rndis_device;
1477aefd80e8SVitaly Kuznetsov 	struct ndis_recv_scale_cap rsscap;
1478aefd80e8SVitaly Kuznetsov 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
1479aefd80e8SVitaly Kuznetsov 	u32 mtu, size;
1480aefd80e8SVitaly Kuznetsov 	u32 num_possible_rss_qs;
1481aefd80e8SVitaly Kuznetsov 	int i, ret;
1482aefd80e8SVitaly Kuznetsov 
1483aefd80e8SVitaly Kuznetsov 	rndis_device = get_rndis_device();
1484aefd80e8SVitaly Kuznetsov 	if (!rndis_device)
1485aefd80e8SVitaly Kuznetsov 		return ERR_PTR(-ENODEV);
1486aefd80e8SVitaly Kuznetsov 
1487aefd80e8SVitaly Kuznetsov 	/* Let the inner driver handle this first to create the netvsc channel
1488aefd80e8SVitaly Kuznetsov 	 * NOTE! Once the channel is created, we may get a receive callback
1489aefd80e8SVitaly Kuznetsov 	 * (RndisFilterOnReceive()) before this call is completed
1490aefd80e8SVitaly Kuznetsov 	 */
1491aefd80e8SVitaly Kuznetsov 	net_device = netvsc_device_add(dev, device_info);
1492aefd80e8SVitaly Kuznetsov 	if (IS_ERR(net_device)) {
1493aefd80e8SVitaly Kuznetsov 		kfree(rndis_device);
1494aefd80e8SVitaly Kuznetsov 		return net_device;
1495aefd80e8SVitaly Kuznetsov 	}
1496aefd80e8SVitaly Kuznetsov 
1497aefd80e8SVitaly Kuznetsov 	/* Initialize the rndis device */
1498aefd80e8SVitaly Kuznetsov 	net_device->max_chn = 1;
1499aefd80e8SVitaly Kuznetsov 	net_device->num_chn = 1;
1500aefd80e8SVitaly Kuznetsov 
1501aefd80e8SVitaly Kuznetsov 	net_device->extension = rndis_device;
1502aefd80e8SVitaly Kuznetsov 	rndis_device->ndev = net;
1503aefd80e8SVitaly Kuznetsov 
1504aefd80e8SVitaly Kuznetsov 	/* Send the rndis initialization message */
1505aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_init_device(rndis_device, net_device);
1506aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1507aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1508aefd80e8SVitaly Kuznetsov 
1509aefd80e8SVitaly Kuznetsov 	/* Get the MTU from the host */
1510aefd80e8SVitaly Kuznetsov 	size = sizeof(u32);
1511aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device(rndis_device, net_device,
1512aefd80e8SVitaly Kuznetsov 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
1513aefd80e8SVitaly Kuznetsov 					&mtu, &size);
1514aefd80e8SVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
1515aefd80e8SVitaly Kuznetsov 		net->mtu = mtu;
1516aefd80e8SVitaly Kuznetsov 
1517aefd80e8SVitaly Kuznetsov 	/* Get the mac address */
1518aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device_mac(rndis_device, net_device);
1519aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1520aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1521aefd80e8SVitaly Kuznetsov 
1522aefd80e8SVitaly Kuznetsov 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
1523aefd80e8SVitaly Kuznetsov 
15240fe554a4SStephen Hemminger 	/* Get friendly name as ifalias*/
15250fe554a4SStephen Hemminger 	if (!net->ifalias)
15260fe554a4SStephen Hemminger 		rndis_get_friendly_name(net, rndis_device, net_device);
15270fe554a4SStephen Hemminger 
1528aefd80e8SVitaly Kuznetsov 	/* Query and set hardware capabilities */
1529aefd80e8SVitaly Kuznetsov 	ret = rndis_netdev_set_hwcaps(rndis_device, net_device);
1530aefd80e8SVitaly Kuznetsov 	if (ret != 0)
15314a0e70aeSKY Srinivasan 		goto err_dev_remv;
15324a0e70aeSKY Srinivasan 
1533867047c4Sstephen hemminger 	rndis_filter_query_device_link_status(rndis_device, net_device);
153495fa0405SHaiyang Zhang 
153593ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
153695fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1537dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
153895fa0405SHaiyang Zhang 
15395b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
154055be9f25SMohammed Gamal 		goto out;
15415b54dac8SHaiyang Zhang 
1542867047c4Sstephen hemminger 	rndis_filter_query_link_speed(rndis_device, net_device);
1543b37879e6SHaiyang Zhang 
15445b54dac8SHaiyang Zhang 	/* vRSS setup */
15455b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
1546867047c4Sstephen hemminger 	ret = rndis_filter_query_device(rndis_device, net_device,
15475b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
15485b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
15495b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
15505b54dac8SHaiyang Zhang 		goto out;
15515b54dac8SHaiyang Zhang 
1552*4cab498fSShradha Gupta 	if (rsscap.num_indirect_tabent &&
1553*4cab498fSShradha Gupta 	    rsscap.num_indirect_tabent <= ITAB_NUM_MAX)
1554*4cab498fSShradha Gupta 		ndc->rx_table_sz = rsscap.num_indirect_tabent;
1555*4cab498fSShradha Gupta 	else
1556*4cab498fSShradha Gupta 		ndc->rx_table_sz = ITAB_NUM;
1557*4cab498fSShradha Gupta 
1558*4cab498fSShradha Gupta 	ndc->rx_table = kcalloc(ndc->rx_table_sz, sizeof(u16), GFP_KERNEL);
1559*4cab498fSShradha Gupta 	if (!ndc->rx_table) {
1560*4cab498fSShradha Gupta 		ret = -ENOMEM;
1561*4cab498fSShradha Gupta 		goto err_dev_remv;
1562*4cab498fSShradha Gupta 	}
1563*4cab498fSShradha Gupta 
156425a39f7fSHaiyang Zhang 	/* This guarantees that num_possible_rss_qs <= num_online_cpus */
156525a39f7fSHaiyang Zhang 	num_possible_rss_qs = min_t(u32, num_online_cpus(),
15663071ada4Sstephen hemminger 				    rsscap.num_recv_que);
15673071ada4Sstephen hemminger 
15683071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
15698ebdcc52SAndrew Schwartzmeyer 
15708ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
15713071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1572ff4a4419Sstephen hemminger 
1573b0689faaSHaiyang Zhang 	if (!netif_is_rxfh_configured(net)) {
1574*4cab498fSShradha Gupta 		for (i = 0; i < ndc->rx_table_sz; i++)
1575b0689faaSHaiyang Zhang 			ndc->rx_table[i] = ethtool_rxfh_indir_default(
157647371300SHaiyang Zhang 						i, net_device->num_chn);
1577b0689faaSHaiyang Zhang 	}
1578ff4a4419Sstephen hemminger 
1579732e4985Sstephen hemminger 	atomic_set(&net_device->open_chn, 1);
15808195b139SStephen Hemminger 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
15815b54dac8SHaiyang Zhang 
15827426b1a5Sstephen hemminger 	for (i = 1; i < net_device->num_chn; i++) {
15837426b1a5Sstephen hemminger 		ret = netvsc_alloc_recv_comp_ring(net_device, i);
15847426b1a5Sstephen hemminger 		if (ret) {
15857426b1a5Sstephen hemminger 			while (--i != 0)
15867426b1a5Sstephen hemminger 				vfree(net_device->chan_table[i].mrc.slots);
15877426b1a5Sstephen hemminger 			goto out;
15887426b1a5Sstephen hemminger 		}
15897426b1a5Sstephen hemminger 	}
15907426b1a5Sstephen hemminger 
15918195b139SStephen Hemminger 	for (i = 1; i < net_device->num_chn; i++)
15928195b139SStephen Hemminger 		netif_napi_add(net, &net_device->chan_table[i].napi,
1593b48b89f9SJakub Kicinski 			       netvsc_poll);
15945b54dac8SHaiyang Zhang 
15953ffe64f1SStephen Hemminger 	return net_device;
15965362855aSVitaly Kuznetsov 
15975b54dac8SHaiyang Zhang out:
15983ffe64f1SStephen Hemminger 	/* setting up multiple channels failed */
159959995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
16005b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
1601b19b4634STakashi Iwai 	return net_device;
16024a0e70aeSKY Srinivasan 
16034a0e70aeSKY Srinivasan err_dev_remv:
16042289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
16059749fed5Sstephen hemminger 	return ERR_PTR(ret);
160695fa0405SHaiyang Zhang }
160795fa0405SHaiyang Zhang 
rndis_filter_device_remove(struct hv_device * dev,struct netvsc_device * net_dev)16082289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
16092289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
161095fa0405SHaiyang Zhang {
161195fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1612*4cab498fSShradha Gupta 	struct net_device *net = hv_get_drvdata(dev);
1613*4cab498fSShradha Gupta 	struct net_device_context *ndc;
1614*4cab498fSShradha Gupta 
1615*4cab498fSShradha Gupta 	ndc = netdev_priv(net);
1616d66ab514SHaiyang Zhang 
161795fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
16180e96460eSStephen Hemminger 	rndis_filter_halt_device(net_dev, rndis_dev);
161995fa0405SHaiyang Zhang 
162095fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
1621*4cab498fSShradha Gupta 
1622*4cab498fSShradha Gupta 	ndc->rx_table_sz = 0;
1623*4cab498fSShradha Gupta 	kfree(ndc->rx_table);
1624*4cab498fSShradha Gupta 	ndc->rx_table = NULL;
162595fa0405SHaiyang Zhang }
162695fa0405SHaiyang Zhang 
rndis_filter_open(struct netvsc_device * nvdev)16272f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
162895fa0405SHaiyang Zhang {
16292f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
163095fa0405SHaiyang Zhang 		return -EINVAL;
163195fa0405SHaiyang Zhang 
16322f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
163395fa0405SHaiyang Zhang }
163495fa0405SHaiyang Zhang 
rndis_filter_close(struct netvsc_device * nvdev)16352f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
163695fa0405SHaiyang Zhang {
16375fccab3bSHaiyang Zhang 	if (!nvdev)
163895fa0405SHaiyang Zhang 		return -EINVAL;
163995fa0405SHaiyang Zhang 
16405fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
164195fa0405SHaiyang Zhang }
1642