195fa0405SHaiyang Zhang /*
295fa0405SHaiyang Zhang  * Copyright (c) 2009, Microsoft Corporation.
395fa0405SHaiyang Zhang  *
495fa0405SHaiyang Zhang  * This program is free software; you can redistribute it and/or modify it
595fa0405SHaiyang Zhang  * under the terms and conditions of the GNU General Public License,
695fa0405SHaiyang Zhang  * version 2, as published by the Free Software Foundation.
795fa0405SHaiyang Zhang  *
895fa0405SHaiyang Zhang  * This program is distributed in the hope it will be useful, but WITHOUT
995fa0405SHaiyang Zhang  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1095fa0405SHaiyang Zhang  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1195fa0405SHaiyang Zhang  * more details.
1295fa0405SHaiyang Zhang  *
1395fa0405SHaiyang Zhang  * You should have received a copy of the GNU General Public License along with
14adf8d3ffSJeff Kirsher  * this program; if not, see <http://www.gnu.org/licenses/>.
1595fa0405SHaiyang Zhang  *
1695fa0405SHaiyang Zhang  * Authors:
1795fa0405SHaiyang Zhang  *   Haiyang Zhang <haiyangz@microsoft.com>
1895fa0405SHaiyang Zhang  *   Hank Janssen  <hjanssen@microsoft.com>
1995fa0405SHaiyang Zhang  */
2095fa0405SHaiyang Zhang #include <linux/kernel.h>
2195fa0405SHaiyang Zhang #include <linux/sched.h>
2295fa0405SHaiyang Zhang #include <linux/wait.h>
2395fa0405SHaiyang Zhang #include <linux/highmem.h>
2495fa0405SHaiyang Zhang #include <linux/slab.h>
2595fa0405SHaiyang Zhang #include <linux/io.h>
2695fa0405SHaiyang Zhang #include <linux/if_ether.h>
2795fa0405SHaiyang Zhang #include <linux/netdevice.h>
281f5f3a75SHaiyang Zhang #include <linux/if_vlan.h>
291ce09e89SHaiyang Zhang #include <linux/nls.h>
30d6472302SStephen Rothwell #include <linux/vmalloc.h>
3195fa0405SHaiyang Zhang 
3295fa0405SHaiyang Zhang #include "hyperv_net.h"
3395fa0405SHaiyang Zhang 
3495fa0405SHaiyang Zhang 
355b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
3695fa0405SHaiyang Zhang struct rndis_request {
3795fa0405SHaiyang Zhang 	struct list_head list_ent;
3895fa0405SHaiyang Zhang 	struct completion  wait_event;
3995fa0405SHaiyang Zhang 
4095fa0405SHaiyang Zhang 	struct rndis_message response_msg;
41a3a6cab5SHaiyang Zhang 	/*
42a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
43a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
44a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
45a3a6cab5SHaiyang Zhang 	 * future.
46a3a6cab5SHaiyang Zhang 	 */
47a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
4895fa0405SHaiyang Zhang 
4995fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
5095fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
510f48917bSHaiyang Zhang 
5295fa0405SHaiyang Zhang 	struct rndis_message request_msg;
530f48917bSHaiyang Zhang 	/*
54a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
55a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
560f48917bSHaiyang Zhang 	 */
57a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
5895fa0405SHaiyang Zhang };
5995fa0405SHaiyang Zhang 
6095fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
6195fa0405SHaiyang Zhang {
6295fa0405SHaiyang Zhang 	struct rndis_device *device;
6395fa0405SHaiyang Zhang 
6495fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
6595fa0405SHaiyang Zhang 	if (!device)
6695fa0405SHaiyang Zhang 		return NULL;
6795fa0405SHaiyang Zhang 
6895fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
6995fa0405SHaiyang Zhang 
7095fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
7195fa0405SHaiyang Zhang 
7295fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
7395fa0405SHaiyang Zhang 
7495fa0405SHaiyang Zhang 	return device;
7595fa0405SHaiyang Zhang }
7695fa0405SHaiyang Zhang 
7795fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
7895fa0405SHaiyang Zhang 					     u32 msg_type,
7995fa0405SHaiyang Zhang 					     u32 msg_len)
8095fa0405SHaiyang Zhang {
8195fa0405SHaiyang Zhang 	struct rndis_request *request;
8295fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
8395fa0405SHaiyang Zhang 	struct rndis_set_request *set;
8495fa0405SHaiyang Zhang 	unsigned long flags;
8595fa0405SHaiyang Zhang 
8695fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
8795fa0405SHaiyang Zhang 	if (!request)
8895fa0405SHaiyang Zhang 		return NULL;
8995fa0405SHaiyang Zhang 
9095fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
9195fa0405SHaiyang Zhang 
9295fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
9395fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
9495fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
9595fa0405SHaiyang Zhang 
965b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
975b54dac8SHaiyang Zhang 
9895fa0405SHaiyang Zhang 	/*
9995fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
10095fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
10195fa0405SHaiyang Zhang 	 * template
10295fa0405SHaiyang Zhang 	 */
10395fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
10495fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
10595fa0405SHaiyang Zhang 
10695fa0405SHaiyang Zhang 	/* Add to the request list */
10795fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
10895fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
10995fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
11095fa0405SHaiyang Zhang 
11195fa0405SHaiyang Zhang 	return request;
11295fa0405SHaiyang Zhang }
11395fa0405SHaiyang Zhang 
11495fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
11595fa0405SHaiyang Zhang 			    struct rndis_request *req)
11695fa0405SHaiyang Zhang {
11795fa0405SHaiyang Zhang 	unsigned long flags;
11895fa0405SHaiyang Zhang 
11995fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12095fa0405SHaiyang Zhang 	list_del(&req->list_ent);
12195fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12295fa0405SHaiyang Zhang 
12395fa0405SHaiyang Zhang 	kfree(req);
12495fa0405SHaiyang Zhang }
12595fa0405SHaiyang Zhang 
12695fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev,
12795fa0405SHaiyang Zhang 			struct rndis_message *rndis_msg)
12895fa0405SHaiyang Zhang {
12995fa0405SHaiyang Zhang 	struct net_device *netdev;
13095fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
13195fa0405SHaiyang Zhang 
13295fa0405SHaiyang Zhang 	net_device = hv_get_drvdata(hv_dev);
13395fa0405SHaiyang Zhang 	netdev = net_device->ndev;
13495fa0405SHaiyang Zhang 
13595fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
13651491167SLinus Walleij 	case RNDIS_MSG_PACKET:
13751491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
13895fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
13995fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14095fa0405SHaiyang Zhang 			   "pkt len %u\n",
14195fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
14395fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
14495fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
14595fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
14695fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
14795fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
14895fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
14995fa0405SHaiyang Zhang 		break;
15095fa0405SHaiyang Zhang 
15151491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15251491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
15395fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
15495fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
15595fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
15695fa0405SHaiyang Zhang 			rndis_msg->msg_len,
15795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
15895fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
15995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16095fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16195fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
16395fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
16495fa0405SHaiyang Zhang 			   max_pkt_per_msg,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
16695fa0405SHaiyang Zhang 			   pkt_alignment_factor);
16795fa0405SHaiyang Zhang 		break;
16895fa0405SHaiyang Zhang 
16951491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17051491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17195fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17295fa0405SHaiyang Zhang 			"buf offset %u)\n",
17395fa0405SHaiyang Zhang 			rndis_msg->msg_len,
17495fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
17595fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
17695fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
17795fa0405SHaiyang Zhang 			   info_buflen,
17895fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
17995fa0405SHaiyang Zhang 			   info_buf_offset);
18095fa0405SHaiyang Zhang 		break;
18195fa0405SHaiyang Zhang 
18251491167SLinus Walleij 	case RNDIS_MSG_SET_C:
18395fa0405SHaiyang Zhang 		netdev_dbg(netdev,
18451491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
18595fa0405SHaiyang Zhang 			rndis_msg->msg_len,
18695fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
18795fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
18895fa0405SHaiyang Zhang 		break;
18995fa0405SHaiyang Zhang 
19051491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19151491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19295fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
19395fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19495fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
19595fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
19695fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
19795fa0405SHaiyang Zhang 		break;
19895fa0405SHaiyang Zhang 
19995fa0405SHaiyang Zhang 	default:
20095fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20195fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20295fa0405SHaiyang Zhang 			rndis_msg->msg_len);
20395fa0405SHaiyang Zhang 		break;
20495fa0405SHaiyang Zhang 	}
20595fa0405SHaiyang Zhang }
20695fa0405SHaiyang Zhang 
20795fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
20895fa0405SHaiyang Zhang 				  struct rndis_request *req)
20995fa0405SHaiyang Zhang {
21095fa0405SHaiyang Zhang 	int ret;
21195fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
212b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
213a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
21495fa0405SHaiyang Zhang 
21595fa0405SHaiyang Zhang 	/* Setup the packet to send it */
21695fa0405SHaiyang Zhang 	packet = &req->pkt;
21795fa0405SHaiyang Zhang 
21895fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
21995fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22095fa0405SHaiyang Zhang 
221a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
22295fa0405SHaiyang Zhang 					PAGE_SHIFT;
223a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
224a9f2e2d6SKY Srinivasan 	pb[0].offset =
22595fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
22695fa0405SHaiyang Zhang 
22799e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
228a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
22999e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
230a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
231a9f2e2d6SKY Srinivasan 			pb[0].offset;
232a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
233a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
234a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
235a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
236a9f2e2d6SKY Srinivasan 			pb[0].len;
23799e3fcfaSHaiyang Zhang 	}
23899e3fcfaSHaiyang Zhang 
2393a3d9a0aSKY Srinivasan 	ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL);
24095fa0405SHaiyang Zhang 	return ret;
24195fa0405SHaiyang Zhang }
24295fa0405SHaiyang Zhang 
2431b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2441b07da51SHaiyang Zhang 				 struct rndis_request *request)
2451b07da51SHaiyang Zhang {
2461b07da51SHaiyang Zhang 	u32 link_status;
2471b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2481b07da51SHaiyang Zhang 
2491b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2501b07da51SHaiyang Zhang 
2511b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2521b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2531b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2541b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2551b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2561b07da51SHaiyang Zhang 	}
2571b07da51SHaiyang Zhang }
2581b07da51SHaiyang Zhang 
25995fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev,
26095fa0405SHaiyang Zhang 				       struct rndis_message *resp)
26195fa0405SHaiyang Zhang {
26295fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
26395fa0405SHaiyang Zhang 	bool found = false;
26495fa0405SHaiyang Zhang 	unsigned long flags;
26595fa0405SHaiyang Zhang 	struct net_device *ndev;
26695fa0405SHaiyang Zhang 
26795fa0405SHaiyang Zhang 	ndev = dev->net_dev->ndev;
26895fa0405SHaiyang Zhang 
26995fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
27095fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
27195fa0405SHaiyang Zhang 		/*
27295fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
27395fa0405SHaiyang Zhang 		 * field
27495fa0405SHaiyang Zhang 		 */
27595fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
27695fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
27795fa0405SHaiyang Zhang 			found = true;
27895fa0405SHaiyang Zhang 			break;
27995fa0405SHaiyang Zhang 		}
28095fa0405SHaiyang Zhang 	}
28195fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
28295fa0405SHaiyang Zhang 
28395fa0405SHaiyang Zhang 	if (found) {
284a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
285a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
28695fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
28795fa0405SHaiyang Zhang 			       resp->msg_len);
2881b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
2891b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
2901b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
2911b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
29295fa0405SHaiyang Zhang 		} else {
29395fa0405SHaiyang Zhang 			netdev_err(ndev,
29495fa0405SHaiyang Zhang 				"rndis response buffer overflow "
29595fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
29695fa0405SHaiyang Zhang 				resp->msg_len,
29786eedaccSKY Srinivasan 				sizeof(struct rndis_message));
29895fa0405SHaiyang Zhang 
29995fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
30051491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
30195fa0405SHaiyang Zhang 				/* does not have a request id field */
30295fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
303007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
30495fa0405SHaiyang Zhang 			} else {
30595fa0405SHaiyang Zhang 				request->response_msg.msg.
30695fa0405SHaiyang Zhang 				init_complete.status =
307007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
30895fa0405SHaiyang Zhang 			}
30995fa0405SHaiyang Zhang 		}
31095fa0405SHaiyang Zhang 
31195fa0405SHaiyang Zhang 		complete(&request->wait_event);
31295fa0405SHaiyang Zhang 	} else {
31395fa0405SHaiyang Zhang 		netdev_err(ndev,
31495fa0405SHaiyang Zhang 			"no rndis request found for this response "
31595fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
31695fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
31795fa0405SHaiyang Zhang 			resp->ndis_msg_type);
31895fa0405SHaiyang Zhang 	}
31995fa0405SHaiyang Zhang }
32095fa0405SHaiyang Zhang 
3211f5f3a75SHaiyang Zhang /*
3221f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3231f5f3a75SHaiyang Zhang  * return NULL if not found.
3241f5f3a75SHaiyang Zhang  */
3251f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3261f5f3a75SHaiyang Zhang {
3271f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3281f5f3a75SHaiyang Zhang 	int len;
3291f5f3a75SHaiyang Zhang 
3301f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3311f5f3a75SHaiyang Zhang 		return NULL;
3321f5f3a75SHaiyang Zhang 
3331f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3341f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3351f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3361f5f3a75SHaiyang Zhang 
3371f5f3a75SHaiyang Zhang 	while (len > 0) {
3381f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3391f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3401f5f3a75SHaiyang Zhang 		len -= ppi->size;
3411f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3421f5f3a75SHaiyang Zhang 	}
3431f5f3a75SHaiyang Zhang 
3441f5f3a75SHaiyang Zhang 	return NULL;
3451f5f3a75SHaiyang Zhang }
3461f5f3a75SHaiyang Zhang 
34710082f98SKY Srinivasan static int rndis_filter_receive_data(struct rndis_device *dev,
34895fa0405SHaiyang Zhang 				   struct rndis_message *msg,
34925b85ee8SKY Srinivasan 				   struct hv_netvsc_packet *pkt,
350c4b20c63SKY Srinivasan 				   void **data,
35125b85ee8SKY Srinivasan 				   struct vmbus_channel *channel)
35295fa0405SHaiyang Zhang {
35395fa0405SHaiyang Zhang 	struct rndis_packet *rndis_pkt;
35495fa0405SHaiyang Zhang 	u32 data_offset;
3551f5f3a75SHaiyang Zhang 	struct ndis_pkt_8021q_info *vlan;
356e3d605edSKY Srinivasan 	struct ndis_tcp_ip_checksum_info *csum_info;
357760d1e36SKY Srinivasan 	u16 vlan_tci = 0;
35895fa0405SHaiyang Zhang 
35995fa0405SHaiyang Zhang 	rndis_pkt = &msg->msg.pkt;
36095fa0405SHaiyang Zhang 
36195fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
36295fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
36395fa0405SHaiyang Zhang 
36495fa0405SHaiyang Zhang 	pkt->total_data_buflen -= data_offset;
3654b8a8bc9SWei Yongjun 
3664b8a8bc9SWei Yongjun 	/*
3674b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3684b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3694b8a8bc9SWei Yongjun 	 */
3704b8a8bc9SWei Yongjun 	if (pkt->total_data_buflen < rndis_pkt->data_len) {
3714b8a8bc9SWei Yongjun 		netdev_err(dev->net_dev->ndev, "rndis message buffer "
3724b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3734b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
3744b8a8bc9SWei Yongjun 			   pkt->total_data_buflen, rndis_pkt->data_len);
37510082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3764b8a8bc9SWei Yongjun 	}
3774b8a8bc9SWei Yongjun 
3784b8a8bc9SWei Yongjun 	/*
3794b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
3804b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
3814b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
3824b8a8bc9SWei Yongjun 	 */
3834b8a8bc9SWei Yongjun 	pkt->total_data_buflen = rndis_pkt->data_len;
384c4b20c63SKY Srinivasan 	*data = (void *)((unsigned long)(*data) + data_offset);
38595fa0405SHaiyang Zhang 
3861f5f3a75SHaiyang Zhang 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
3871f5f3a75SHaiyang Zhang 	if (vlan) {
388760d1e36SKY Srinivasan 		vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
3891f5f3a75SHaiyang Zhang 			(vlan->pri << VLAN_PRIO_SHIFT);
3901f5f3a75SHaiyang Zhang 	}
3911f5f3a75SHaiyang Zhang 
392e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
39310082f98SKY Srinivasan 	return netvsc_recv_callback(dev->net_dev->dev, pkt, data,
394760d1e36SKY Srinivasan 				    csum_info, channel, vlan_tci);
39595fa0405SHaiyang Zhang }
39695fa0405SHaiyang Zhang 
39795fa0405SHaiyang Zhang int rndis_filter_receive(struct hv_device *dev,
39825b85ee8SKY Srinivasan 				struct hv_netvsc_packet	*pkt,
399c4b20c63SKY Srinivasan 				void **data,
40025b85ee8SKY Srinivasan 				struct vmbus_channel *channel)
40195fa0405SHaiyang Zhang {
40295fa0405SHaiyang Zhang 	struct netvsc_device *net_dev = hv_get_drvdata(dev);
40395fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev;
404ef31bef6SHaiyang Zhang 	struct rndis_message *rndis_msg;
40595fa0405SHaiyang Zhang 	struct net_device *ndev;
40663f6921dSHaiyang Zhang 	int ret = 0;
40795fa0405SHaiyang Zhang 
40863f6921dSHaiyang Zhang 	if (!net_dev) {
40910082f98SKY Srinivasan 		ret = NVSP_STAT_FAIL;
41063f6921dSHaiyang Zhang 		goto exit;
41163f6921dSHaiyang Zhang 	}
41295fa0405SHaiyang Zhang 
41395fa0405SHaiyang Zhang 	ndev = net_dev->ndev;
41495fa0405SHaiyang Zhang 
41595fa0405SHaiyang Zhang 	/* Make sure the rndis device state is initialized */
41695fa0405SHaiyang Zhang 	if (!net_dev->extension) {
41795fa0405SHaiyang Zhang 		netdev_err(ndev, "got rndis message but no rndis device - "
41895fa0405SHaiyang Zhang 			  "dropping this message!\n");
41910082f98SKY Srinivasan 		ret = NVSP_STAT_FAIL;
42063f6921dSHaiyang Zhang 		goto exit;
42195fa0405SHaiyang Zhang 	}
42295fa0405SHaiyang Zhang 
42395fa0405SHaiyang Zhang 	rndis_dev = (struct rndis_device *)net_dev->extension;
42495fa0405SHaiyang Zhang 	if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
42595fa0405SHaiyang Zhang 		netdev_err(ndev, "got rndis message but rndis device "
42695fa0405SHaiyang Zhang 			   "uninitialized...dropping this message!\n");
42710082f98SKY Srinivasan 		ret = NVSP_STAT_FAIL;
42863f6921dSHaiyang Zhang 		goto exit;
42995fa0405SHaiyang Zhang 	}
43095fa0405SHaiyang Zhang 
431c4b20c63SKY Srinivasan 	rndis_msg = *data;
43295fa0405SHaiyang Zhang 
4333f300ff4SSimon Xiao 	if (netif_msg_rx_err(net_dev->nd_ctx))
434ef31bef6SHaiyang Zhang 		dump_rndis_message(dev, rndis_msg);
43595fa0405SHaiyang Zhang 
436ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
43751491167SLinus Walleij 	case RNDIS_MSG_PACKET:
43895fa0405SHaiyang Zhang 		/* data msg */
43910082f98SKY Srinivasan 		ret = rndis_filter_receive_data(rndis_dev, rndis_msg, pkt,
440c4b20c63SKY Srinivasan 						data, channel);
44195fa0405SHaiyang Zhang 		break;
44295fa0405SHaiyang Zhang 
44351491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
44451491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
44551491167SLinus Walleij 	case RNDIS_MSG_SET_C:
44695fa0405SHaiyang Zhang 		/* completion msgs */
447ef31bef6SHaiyang Zhang 		rndis_filter_receive_response(rndis_dev, rndis_msg);
44895fa0405SHaiyang Zhang 		break;
44995fa0405SHaiyang Zhang 
45051491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
45195fa0405SHaiyang Zhang 		/* notification msgs */
4523a494e71SHaiyang Zhang 		netvsc_linkstatus_callback(dev, rndis_msg);
45395fa0405SHaiyang Zhang 		break;
45495fa0405SHaiyang Zhang 	default:
45595fa0405SHaiyang Zhang 		netdev_err(ndev,
45695fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
457ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
458ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
45995fa0405SHaiyang Zhang 		break;
46095fa0405SHaiyang Zhang 	}
46195fa0405SHaiyang Zhang 
46263f6921dSHaiyang Zhang exit:
46363f6921dSHaiyang Zhang 	return ret;
46495fa0405SHaiyang Zhang }
46595fa0405SHaiyang Zhang 
46695fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
46795fa0405SHaiyang Zhang 				  void *result, u32 *result_size)
46895fa0405SHaiyang Zhang {
46995fa0405SHaiyang Zhang 	struct rndis_request *request;
47095fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
47195fa0405SHaiyang Zhang 	struct rndis_query_request *query;
47295fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
47395fa0405SHaiyang Zhang 	int ret = 0;
474999028ccSNicholas Mc Guire 	unsigned long t;
47595fa0405SHaiyang Zhang 
47695fa0405SHaiyang Zhang 	if (!result)
47795fa0405SHaiyang Zhang 		return -EINVAL;
47895fa0405SHaiyang Zhang 
47995fa0405SHaiyang Zhang 	*result_size = 0;
48051491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
48195fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
48295fa0405SHaiyang Zhang 	if (!request) {
48395fa0405SHaiyang Zhang 		ret = -ENOMEM;
48495fa0405SHaiyang Zhang 		goto cleanup;
48595fa0405SHaiyang Zhang 	}
48695fa0405SHaiyang Zhang 
48795fa0405SHaiyang Zhang 	/* Setup the rndis query */
48895fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
48995fa0405SHaiyang Zhang 	query->oid = oid;
49095fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
49195fa0405SHaiyang Zhang 	query->info_buflen = 0;
49295fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
49395fa0405SHaiyang Zhang 
4945b54dac8SHaiyang Zhang 	if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
4955b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
4965b54dac8SHaiyang Zhang 
4975b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
4985b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
4995b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5005b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5015b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5025b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5035b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5045b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5055b54dac8SHaiyang Zhang 	}
5065b54dac8SHaiyang Zhang 
50795fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
50895fa0405SHaiyang Zhang 	if (ret != 0)
50995fa0405SHaiyang Zhang 		goto cleanup;
51095fa0405SHaiyang Zhang 
51195fa0405SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
51295fa0405SHaiyang Zhang 	if (t == 0) {
51395fa0405SHaiyang Zhang 		ret = -ETIMEDOUT;
51495fa0405SHaiyang Zhang 		goto cleanup;
51595fa0405SHaiyang Zhang 	}
51695fa0405SHaiyang Zhang 
51795fa0405SHaiyang Zhang 	/* Copy the response back */
51895fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
51995fa0405SHaiyang Zhang 
52095fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
52195fa0405SHaiyang Zhang 		ret = -1;
52295fa0405SHaiyang Zhang 		goto cleanup;
52395fa0405SHaiyang Zhang 	}
52495fa0405SHaiyang Zhang 
52595fa0405SHaiyang Zhang 	memcpy(result,
52695fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
52795fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
52895fa0405SHaiyang Zhang 	       query_complete->info_buflen);
52995fa0405SHaiyang Zhang 
53095fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
53195fa0405SHaiyang Zhang 
53295fa0405SHaiyang Zhang cleanup:
53395fa0405SHaiyang Zhang 	if (request)
53495fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
53595fa0405SHaiyang Zhang 
53695fa0405SHaiyang Zhang 	return ret;
53795fa0405SHaiyang Zhang }
53895fa0405SHaiyang Zhang 
53995fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev)
54095fa0405SHaiyang Zhang {
54195fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
54295fa0405SHaiyang Zhang 
54395fa0405SHaiyang Zhang 	return rndis_filter_query_device(dev,
54495fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
54595fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
54695fa0405SHaiyang Zhang }
54795fa0405SHaiyang Zhang 
5481ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5491ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5501ce09e89SHaiyang Zhang 
5511ce09e89SHaiyang Zhang int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac)
5521ce09e89SHaiyang Zhang {
5531ce09e89SHaiyang Zhang 	struct netvsc_device *nvdev = hv_get_drvdata(hdev);
5541ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
5551ce09e89SHaiyang Zhang 	struct net_device *ndev = nvdev->ndev;
5561ce09e89SHaiyang Zhang 	struct rndis_request *request;
5571ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
5581ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
5591ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
5601ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
5611ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
5621ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
5631ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
564999028ccSNicholas Mc Guire 	int ret;
565999028ccSNicholas Mc Guire 	unsigned long t;
5661ce09e89SHaiyang Zhang 
5671ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
5681ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
5691ce09e89SHaiyang Zhang 	if (!request)
5701ce09e89SHaiyang Zhang 		return -ENOMEM;
5711ce09e89SHaiyang Zhang 
5721ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
5731ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
5741ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
5751ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
5761ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
5771ce09e89SHaiyang Zhang 
5781ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
5791ce09e89SHaiyang Zhang 		set->info_buf_offset);
5801ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
5811ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
5821ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
5831ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
5841ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
5851ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
5861ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
5871ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
5881ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
5891ce09e89SHaiyang Zhang 
5901ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
5911ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
5921ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
5931ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
5941ce09e89SHaiyang Zhang 	if (ret < 0)
5951ce09e89SHaiyang Zhang 		goto cleanup;
5961ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
5971ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
5981ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
5991ce09e89SHaiyang Zhang 	if (ret < 0)
6001ce09e89SHaiyang Zhang 		goto cleanup;
6011ce09e89SHaiyang Zhang 
6021ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6031ce09e89SHaiyang Zhang 	if (ret != 0)
6041ce09e89SHaiyang Zhang 		goto cleanup;
6051ce09e89SHaiyang Zhang 
6061ce09e89SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
6071ce09e89SHaiyang Zhang 	if (t == 0) {
6081ce09e89SHaiyang Zhang 		netdev_err(ndev, "timeout before we got a set response...\n");
6091ce09e89SHaiyang Zhang 		/*
6101ce09e89SHaiyang Zhang 		 * can't put_rndis_request, since we may still receive a
6111ce09e89SHaiyang Zhang 		 * send-completion.
6121ce09e89SHaiyang Zhang 		 */
6131ce09e89SHaiyang Zhang 		return -EBUSY;
6141ce09e89SHaiyang Zhang 	} else {
6151ce09e89SHaiyang Zhang 		set_complete = &request->response_msg.msg.set_complete;
616b02a8067SHaiyang Zhang 		if (set_complete->status != RNDIS_STATUS_SUCCESS) {
617b02a8067SHaiyang Zhang 			netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
618b02a8067SHaiyang Zhang 				   set_complete->status);
6191ce09e89SHaiyang Zhang 			ret = -EINVAL;
6201ce09e89SHaiyang Zhang 		}
621b02a8067SHaiyang Zhang 	}
6221ce09e89SHaiyang Zhang 
6231ce09e89SHaiyang Zhang cleanup:
6241ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6251ce09e89SHaiyang Zhang 	return ret;
6261ce09e89SHaiyang Zhang }
6271ce09e89SHaiyang Zhang 
628da19fcd0SLad, Prabhakar static int
629da19fcd0SLad, Prabhakar rndis_filter_set_offload_params(struct hv_device *hdev,
6304a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6314a0e70aeSKY Srinivasan {
6324a0e70aeSKY Srinivasan 	struct netvsc_device *nvdev = hv_get_drvdata(hdev);
6334a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6344a0e70aeSKY Srinivasan 	struct net_device *ndev = nvdev->ndev;
6354a0e70aeSKY Srinivasan 	struct rndis_request *request;
6364a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6374a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6384a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6394a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
640999028ccSNicholas Mc Guire 	int ret;
641999028ccSNicholas Mc Guire 	unsigned long t;
642af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
643af9893a3SKY Srinivasan 
644af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
645af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
646af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
647af9893a3SKY Srinivasan 		 * UDP checksum offload.
648af9893a3SKY Srinivasan 		 */
649af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
650af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
651af9893a3SKY Srinivasan 	}
6524a0e70aeSKY Srinivasan 
6534a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6544a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6554a0e70aeSKY Srinivasan 	if (!request)
6564a0e70aeSKY Srinivasan 		return -ENOMEM;
6574a0e70aeSKY Srinivasan 
6584a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6594a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6604a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6614a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6624a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
6634a0e70aeSKY Srinivasan 
6644a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
6654a0e70aeSKY Srinivasan 				set->info_buf_offset);
6664a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
6674a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
6684a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
6694a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
6704a0e70aeSKY Srinivasan 
6714a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
6724a0e70aeSKY Srinivasan 	if (ret != 0)
6734a0e70aeSKY Srinivasan 		goto cleanup;
6744a0e70aeSKY Srinivasan 
6754a0e70aeSKY Srinivasan 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
6764a0e70aeSKY Srinivasan 	if (t == 0) {
6774a0e70aeSKY Srinivasan 		netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n");
6784a0e70aeSKY Srinivasan 		/* can't put_rndis_request, since we may still receive a
6794a0e70aeSKY Srinivasan 		 * send-completion.
6804a0e70aeSKY Srinivasan 		 */
6814a0e70aeSKY Srinivasan 		return -EBUSY;
6824a0e70aeSKY Srinivasan 	} else {
6834a0e70aeSKY Srinivasan 		set_complete = &request->response_msg.msg.set_complete;
6844a0e70aeSKY Srinivasan 		if (set_complete->status != RNDIS_STATUS_SUCCESS) {
685af9893a3SKY Srinivasan 			netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
6864a0e70aeSKY Srinivasan 				   set_complete->status);
6874a0e70aeSKY Srinivasan 			ret = -EINVAL;
6884a0e70aeSKY Srinivasan 		}
6894a0e70aeSKY Srinivasan 	}
6904a0e70aeSKY Srinivasan 
6914a0e70aeSKY Srinivasan cleanup:
6924a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
6934a0e70aeSKY Srinivasan 	return ret;
6944a0e70aeSKY Srinivasan }
6951ce09e89SHaiyang Zhang 
6965b54dac8SHaiyang Zhang u8 netvsc_hash_key[HASH_KEYLEN] = {
6975b54dac8SHaiyang Zhang 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
6985b54dac8SHaiyang Zhang 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
6995b54dac8SHaiyang Zhang 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
7005b54dac8SHaiyang Zhang 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
7015b54dac8SHaiyang Zhang 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
7025b54dac8SHaiyang Zhang };
7035b54dac8SHaiyang Zhang 
704da19fcd0SLad, Prabhakar static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
7055b54dac8SHaiyang Zhang {
7065b54dac8SHaiyang Zhang 	struct net_device *ndev = rdev->net_dev->ndev;
7075b54dac8SHaiyang Zhang 	struct rndis_request *request;
7085b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7095b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7105b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
7115b54dac8SHaiyang Zhang 		     4*ITAB_NUM + HASH_KEYLEN;
7125b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7135b54dac8SHaiyang Zhang 	u32 *itab;
7145b54dac8SHaiyang Zhang 	u8 *keyp;
715999028ccSNicholas Mc Guire 	int i, ret;
716999028ccSNicholas Mc Guire 	unsigned long t;
7175b54dac8SHaiyang Zhang 
7185b54dac8SHaiyang Zhang 	request = get_rndis_request(
7195b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7205b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7215b54dac8SHaiyang Zhang 	if (!request)
7225b54dac8SHaiyang Zhang 		return -ENOMEM;
7235b54dac8SHaiyang Zhang 
7245b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7255b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7265b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7275b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7285b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7295b54dac8SHaiyang Zhang 
7305b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7315b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7325b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7335b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7345b54dac8SHaiyang Zhang 	rssp->flag = 0;
7355b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7364c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7374c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7385b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7395b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
7405b54dac8SHaiyang Zhang 	rssp->hashkey_size = HASH_KEYLEN;
7415b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7425b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7435b54dac8SHaiyang Zhang 
7445b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7455b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7465b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
7475b54dac8SHaiyang Zhang 		itab[i] = i % num_queue;
7485b54dac8SHaiyang Zhang 
7495b54dac8SHaiyang Zhang 	/* Set hask key values */
7505b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
7515b54dac8SHaiyang Zhang 	for (i = 0; i < HASH_KEYLEN; i++)
7525b54dac8SHaiyang Zhang 		keyp[i] = netvsc_hash_key[i];
7535b54dac8SHaiyang Zhang 
7545b54dac8SHaiyang Zhang 
7555b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7565b54dac8SHaiyang Zhang 	if (ret != 0)
7575b54dac8SHaiyang Zhang 		goto cleanup;
7585b54dac8SHaiyang Zhang 
7595b54dac8SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
7605b54dac8SHaiyang Zhang 	if (t == 0) {
7615b54dac8SHaiyang Zhang 		netdev_err(ndev, "timeout before we got a set response...\n");
7625b54dac8SHaiyang Zhang 		/* can't put_rndis_request, since we may still receive a
7635b54dac8SHaiyang Zhang 		 * send-completion.
7645b54dac8SHaiyang Zhang 		 */
7655b54dac8SHaiyang Zhang 		return -ETIMEDOUT;
7665b54dac8SHaiyang Zhang 	} else {
7675b54dac8SHaiyang Zhang 		set_complete = &request->response_msg.msg.set_complete;
7685b54dac8SHaiyang Zhang 		if (set_complete->status != RNDIS_STATUS_SUCCESS) {
7695b54dac8SHaiyang Zhang 			netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7705b54dac8SHaiyang Zhang 				   set_complete->status);
7715b54dac8SHaiyang Zhang 			ret = -EINVAL;
7725b54dac8SHaiyang Zhang 		}
7735b54dac8SHaiyang Zhang 	}
7745b54dac8SHaiyang Zhang 
7755b54dac8SHaiyang Zhang cleanup:
7765b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7775b54dac8SHaiyang Zhang 	return ret;
7785b54dac8SHaiyang Zhang }
7795b54dac8SHaiyang Zhang 
7805b54dac8SHaiyang Zhang 
78195fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev)
78295fa0405SHaiyang Zhang {
78395fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
78495fa0405SHaiyang Zhang 	u32 link_status;
78595fa0405SHaiyang Zhang 	int ret;
78695fa0405SHaiyang Zhang 
78795fa0405SHaiyang Zhang 	ret = rndis_filter_query_device(dev,
78895fa0405SHaiyang Zhang 				      RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
78995fa0405SHaiyang Zhang 				      &link_status, &size);
79095fa0405SHaiyang Zhang 
79195fa0405SHaiyang Zhang 	return ret;
79295fa0405SHaiyang Zhang }
79395fa0405SHaiyang Zhang 
794d426b2e3SHaiyang Zhang int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter)
79595fa0405SHaiyang Zhang {
79695fa0405SHaiyang Zhang 	struct rndis_request *request;
79795fa0405SHaiyang Zhang 	struct rndis_set_request *set;
79895fa0405SHaiyang Zhang 	struct rndis_set_complete *set_complete;
79995fa0405SHaiyang Zhang 	u32 status;
800999028ccSNicholas Mc Guire 	int ret;
801999028ccSNicholas Mc Guire 	unsigned long t;
80295fa0405SHaiyang Zhang 	struct net_device *ndev;
80395fa0405SHaiyang Zhang 
80495fa0405SHaiyang Zhang 	ndev = dev->net_dev->ndev;
80595fa0405SHaiyang Zhang 
80651491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
80795fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
80895fa0405SHaiyang Zhang 			sizeof(u32));
80995fa0405SHaiyang Zhang 	if (!request) {
81095fa0405SHaiyang Zhang 		ret = -ENOMEM;
81195fa0405SHaiyang Zhang 		goto cleanup;
81295fa0405SHaiyang Zhang 	}
81395fa0405SHaiyang Zhang 
81495fa0405SHaiyang Zhang 	/* Setup the rndis set */
81595fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
81695fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
81795fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
81895fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
81995fa0405SHaiyang Zhang 
82095fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
82195fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
82295fa0405SHaiyang Zhang 
82395fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
82495fa0405SHaiyang Zhang 	if (ret != 0)
82595fa0405SHaiyang Zhang 		goto cleanup;
82695fa0405SHaiyang Zhang 
82795fa0405SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
82895fa0405SHaiyang Zhang 
82995fa0405SHaiyang Zhang 	if (t == 0) {
83095fa0405SHaiyang Zhang 		netdev_err(ndev,
83195fa0405SHaiyang Zhang 			"timeout before we got a set response...\n");
832ea496374SHaiyang Zhang 		ret = -ETIMEDOUT;
83395fa0405SHaiyang Zhang 		/*
83495fa0405SHaiyang Zhang 		 * We can't deallocate the request since we may still receive a
83595fa0405SHaiyang Zhang 		 * send completion for it.
83695fa0405SHaiyang Zhang 		 */
83795fa0405SHaiyang Zhang 		goto exit;
83895fa0405SHaiyang Zhang 	} else {
83995fa0405SHaiyang Zhang 		set_complete = &request->response_msg.msg.set_complete;
84095fa0405SHaiyang Zhang 		status = set_complete->status;
84195fa0405SHaiyang Zhang 	}
84295fa0405SHaiyang Zhang 
84395fa0405SHaiyang Zhang cleanup:
84495fa0405SHaiyang Zhang 	if (request)
84595fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
84695fa0405SHaiyang Zhang exit:
84795fa0405SHaiyang Zhang 	return ret;
84895fa0405SHaiyang Zhang }
84995fa0405SHaiyang Zhang 
85095fa0405SHaiyang Zhang 
85195fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev)
85295fa0405SHaiyang Zhang {
85395fa0405SHaiyang Zhang 	struct rndis_request *request;
85495fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
85595fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
85695fa0405SHaiyang Zhang 	u32 status;
857999028ccSNicholas Mc Guire 	int ret;
858999028ccSNicholas Mc Guire 	unsigned long t;
8597c3877f2SHaiyang Zhang 	struct netvsc_device *nvdev = dev->net_dev;
86095fa0405SHaiyang Zhang 
86151491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
86295fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
86395fa0405SHaiyang Zhang 	if (!request) {
86495fa0405SHaiyang Zhang 		ret = -ENOMEM;
86595fa0405SHaiyang Zhang 		goto cleanup;
86695fa0405SHaiyang Zhang 	}
86795fa0405SHaiyang Zhang 
86895fa0405SHaiyang Zhang 	/* Setup the rndis set */
86995fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
87095fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
87195fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
872fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
87395fa0405SHaiyang Zhang 
87495fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
87595fa0405SHaiyang Zhang 
87695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
87795fa0405SHaiyang Zhang 	if (ret != 0) {
87895fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
87995fa0405SHaiyang Zhang 		goto cleanup;
88095fa0405SHaiyang Zhang 	}
88195fa0405SHaiyang Zhang 
88295fa0405SHaiyang Zhang 
88395fa0405SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
88495fa0405SHaiyang Zhang 
88595fa0405SHaiyang Zhang 	if (t == 0) {
88695fa0405SHaiyang Zhang 		ret = -ETIMEDOUT;
88795fa0405SHaiyang Zhang 		goto cleanup;
88895fa0405SHaiyang Zhang 	}
88995fa0405SHaiyang Zhang 
89095fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
89195fa0405SHaiyang Zhang 	status = init_complete->status;
89295fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
89395fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
8947c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
8957c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
89695fa0405SHaiyang Zhang 		ret = 0;
89795fa0405SHaiyang Zhang 	} else {
89895fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
89995fa0405SHaiyang Zhang 		ret = -EINVAL;
90095fa0405SHaiyang Zhang 	}
90195fa0405SHaiyang Zhang 
90295fa0405SHaiyang Zhang cleanup:
90395fa0405SHaiyang Zhang 	if (request)
90495fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
90595fa0405SHaiyang Zhang 
90695fa0405SHaiyang Zhang 	return ret;
90795fa0405SHaiyang Zhang }
90895fa0405SHaiyang Zhang 
90995fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
91095fa0405SHaiyang Zhang {
91195fa0405SHaiyang Zhang 	struct rndis_request *request;
91295fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
913ae9e63bbSHaiyang Zhang 	struct netvsc_device *nvdev = dev->net_dev;
914ae9e63bbSHaiyang Zhang 	struct hv_device *hdev = nvdev->dev;
915ae9e63bbSHaiyang Zhang 	ulong flags;
91695fa0405SHaiyang Zhang 
91795fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
91851491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
91995fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
92095fa0405SHaiyang Zhang 	if (!request)
92195fa0405SHaiyang Zhang 		goto cleanup;
92295fa0405SHaiyang Zhang 
92395fa0405SHaiyang Zhang 	/* Setup the rndis set */
92495fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
92595fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
92695fa0405SHaiyang Zhang 
92795fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
92895fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
92995fa0405SHaiyang Zhang 
93095fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
93195fa0405SHaiyang Zhang 
93295fa0405SHaiyang Zhang cleanup:
933ae9e63bbSHaiyang Zhang 	spin_lock_irqsave(&hdev->channel->inbound_lock, flags);
934ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
935ae9e63bbSHaiyang Zhang 	spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
936ae9e63bbSHaiyang Zhang 
937ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
938ae9e63bbSHaiyang Zhang 	wait_event(nvdev->wait_drain,
939ae9e63bbSHaiyang Zhang 		atomic_read(&nvdev->num_outstanding_sends) == 0);
940ae9e63bbSHaiyang Zhang 
94195fa0405SHaiyang Zhang 	if (request)
94295fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
94395fa0405SHaiyang Zhang 	return;
94495fa0405SHaiyang Zhang }
94595fa0405SHaiyang Zhang 
94695fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
94795fa0405SHaiyang Zhang {
94895fa0405SHaiyang Zhang 	int ret;
94995fa0405SHaiyang Zhang 
95095fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
95195fa0405SHaiyang Zhang 		return 0;
95295fa0405SHaiyang Zhang 
95395fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
95495fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
95595fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
95695fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
95795fa0405SHaiyang Zhang 	if (ret == 0)
95895fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
95995fa0405SHaiyang Zhang 
96095fa0405SHaiyang Zhang 	return ret;
96195fa0405SHaiyang Zhang }
96295fa0405SHaiyang Zhang 
96395fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
96495fa0405SHaiyang Zhang {
96595fa0405SHaiyang Zhang 	int ret;
96695fa0405SHaiyang Zhang 
96795fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
96895fa0405SHaiyang Zhang 		return 0;
96995fa0405SHaiyang Zhang 
97095fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
971c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
972c3582a2cSHaiyang Zhang 		ret = 0;
973c3582a2cSHaiyang Zhang 
97495fa0405SHaiyang Zhang 	if (ret == 0)
97595fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
97695fa0405SHaiyang Zhang 
97795fa0405SHaiyang Zhang 	return ret;
97895fa0405SHaiyang Zhang }
97995fa0405SHaiyang Zhang 
9805b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
9815b54dac8SHaiyang Zhang {
9825b54dac8SHaiyang Zhang 	struct netvsc_device *nvscdev;
9835b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
9845b54dac8SHaiyang Zhang 	int ret;
985b3e6b82aSKY Srinivasan 	unsigned long flags;
9865b54dac8SHaiyang Zhang 
9875b54dac8SHaiyang Zhang 	nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj);
9885b54dac8SHaiyang Zhang 
989b3e6b82aSKY Srinivasan 	spin_lock_irqsave(&nvscdev->sc_lock, flags);
990b3e6b82aSKY Srinivasan 	nvscdev->num_sc_offered--;
991b3e6b82aSKY Srinivasan 	spin_unlock_irqrestore(&nvscdev->sc_lock, flags);
992b3e6b82aSKY Srinivasan 	if (nvscdev->num_sc_offered == 0)
993b3e6b82aSKY Srinivasan 		complete(&nvscdev->channel_init_wait);
994b3e6b82aSKY Srinivasan 
9955b54dac8SHaiyang Zhang 	if (chn_index >= nvscdev->num_chn)
9965b54dac8SHaiyang Zhang 		return;
9975b54dac8SHaiyang Zhang 
9985b54dac8SHaiyang Zhang 	set_per_channel_state(new_sc, nvscdev->sub_cb_buf + (chn_index - 1) *
9995b54dac8SHaiyang Zhang 			      NETVSC_PACKET_SIZE);
10005b54dac8SHaiyang Zhang 
10015b54dac8SHaiyang Zhang 	ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
10025b54dac8SHaiyang Zhang 			 nvscdev->ring_size * PAGE_SIZE, NULL, 0,
10035b54dac8SHaiyang Zhang 			 netvsc_channel_cb, new_sc);
10045b54dac8SHaiyang Zhang 
10055b54dac8SHaiyang Zhang 	if (ret == 0)
10065b54dac8SHaiyang Zhang 		nvscdev->chn_table[chn_index] = new_sc;
10075b54dac8SHaiyang Zhang }
10085b54dac8SHaiyang Zhang 
100995fa0405SHaiyang Zhang int rndis_filter_device_add(struct hv_device *dev,
101095fa0405SHaiyang Zhang 				  void *additional_info)
101195fa0405SHaiyang Zhang {
101295fa0405SHaiyang Zhang 	int ret;
101395fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
101495fa0405SHaiyang Zhang 	struct rndis_device *rndis_device;
101595fa0405SHaiyang Zhang 	struct netvsc_device_info *device_info = additional_info;
10164a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
10175b54dac8SHaiyang Zhang 	struct nvsp_message *init_packet;
1018999028ccSNicholas Mc Guire 	unsigned long t;
10195b54dac8SHaiyang Zhang 	struct ndis_recv_scale_cap rsscap;
10205b54dac8SHaiyang Zhang 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
10214d3c9d37SHaiyang Zhang 	u32 mtu, size;
1022e01ec219SKY Srinivasan 	u32 num_rss_qs;
1023b3e6b82aSKY Srinivasan 	u32 sc_delta;
1024e01ec219SKY Srinivasan 	const struct cpumask *node_cpu_mask;
1025e01ec219SKY Srinivasan 	u32 num_possible_rss_qs;
1026b3e6b82aSKY Srinivasan 	unsigned long flags;
102795fa0405SHaiyang Zhang 
102895fa0405SHaiyang Zhang 	rndis_device = get_rndis_device();
102995fa0405SHaiyang Zhang 	if (!rndis_device)
103095fa0405SHaiyang Zhang 		return -ENODEV;
103195fa0405SHaiyang Zhang 
103295fa0405SHaiyang Zhang 	/*
103395fa0405SHaiyang Zhang 	 * Let the inner driver handle this first to create the netvsc channel
103495fa0405SHaiyang Zhang 	 * NOTE! Once the channel is created, we may get a receive callback
103595fa0405SHaiyang Zhang 	 * (RndisFilterOnReceive()) before this call is completed
103695fa0405SHaiyang Zhang 	 */
103795fa0405SHaiyang Zhang 	ret = netvsc_device_add(dev, additional_info);
103895fa0405SHaiyang Zhang 	if (ret != 0) {
103995fa0405SHaiyang Zhang 		kfree(rndis_device);
104095fa0405SHaiyang Zhang 		return ret;
104195fa0405SHaiyang Zhang 	}
104295fa0405SHaiyang Zhang 
104395fa0405SHaiyang Zhang 
104495fa0405SHaiyang Zhang 	/* Initialize the rndis device */
104595fa0405SHaiyang Zhang 	net_device = hv_get_drvdata(dev);
104659995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
10475b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
104895fa0405SHaiyang Zhang 
1049b3e6b82aSKY Srinivasan 	spin_lock_init(&net_device->sc_lock);
1050b3e6b82aSKY Srinivasan 
105195fa0405SHaiyang Zhang 	net_device->extension = rndis_device;
105295fa0405SHaiyang Zhang 	rndis_device->net_dev = net_device;
105395fa0405SHaiyang Zhang 
105495fa0405SHaiyang Zhang 	/* Send the rndis initialization message */
105595fa0405SHaiyang Zhang 	ret = rndis_filter_init_device(rndis_device);
105695fa0405SHaiyang Zhang 	if (ret != 0) {
10575243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
10585243e7bdSHaiyang Zhang 		return ret;
105995fa0405SHaiyang Zhang 	}
106095fa0405SHaiyang Zhang 
10614d3c9d37SHaiyang Zhang 	/* Get the MTU from the host */
10624d3c9d37SHaiyang Zhang 	size = sizeof(u32);
10634d3c9d37SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
10644d3c9d37SHaiyang Zhang 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
10654d3c9d37SHaiyang Zhang 					&mtu, &size);
1066f9cbce34SHaiyang Zhang 	if (ret == 0 && size == sizeof(u32) && mtu < net_device->ndev->mtu)
10674d3c9d37SHaiyang Zhang 		net_device->ndev->mtu = mtu;
10684d3c9d37SHaiyang Zhang 
106995fa0405SHaiyang Zhang 	/* Get the mac address */
107095fa0405SHaiyang Zhang 	ret = rndis_filter_query_device_mac(rndis_device);
107195fa0405SHaiyang Zhang 	if (ret != 0) {
10725243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
10735243e7bdSHaiyang Zhang 		return ret;
107495fa0405SHaiyang Zhang 	}
107595fa0405SHaiyang Zhang 
107695fa0405SHaiyang Zhang 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
107795fa0405SHaiyang Zhang 
10784a0e70aeSKY Srinivasan 	/* Turn on the offloads; the host supports all of the relevant
10794a0e70aeSKY Srinivasan 	 * offloads.
10804a0e70aeSKY Srinivasan 	 */
10814a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
10824a0e70aeSKY Srinivasan 	/* A value of zero means "no change"; now turn on what we
10834a0e70aeSKY Srinivasan 	 * want.
10844a0e70aeSKY Srinivasan 	 */
10854a0e70aeSKY Srinivasan 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10864a0e70aeSKY Srinivasan 	offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10874a0e70aeSKY Srinivasan 	offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10884a0e70aeSKY Srinivasan 	offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10894a0e70aeSKY Srinivasan 	offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10904a0e70aeSKY Srinivasan 	offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
10914a0e70aeSKY Srinivasan 
10924a0e70aeSKY Srinivasan 
10934a0e70aeSKY Srinivasan 	ret = rndis_filter_set_offload_params(dev, &offloads);
10944a0e70aeSKY Srinivasan 	if (ret)
10954a0e70aeSKY Srinivasan 		goto err_dev_remv;
10964a0e70aeSKY Srinivasan 
109795fa0405SHaiyang Zhang 	rndis_filter_query_device_link_status(rndis_device);
109895fa0405SHaiyang Zhang 
109995fa0405SHaiyang Zhang 	device_info->link_state = rndis_device->link_state;
110095fa0405SHaiyang Zhang 
110195fa0405SHaiyang Zhang 	dev_info(&dev->device, "Device MAC %pM link state %s\n",
110295fa0405SHaiyang Zhang 		 rndis_device->hw_mac_adr,
110395fa0405SHaiyang Zhang 		 device_info->link_state ? "down" : "up");
110495fa0405SHaiyang Zhang 
11055b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
11065b54dac8SHaiyang Zhang 		return 0;
11075b54dac8SHaiyang Zhang 
11085b54dac8SHaiyang Zhang 	/* vRSS setup */
11095b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
11105b54dac8SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
11115b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
11125b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
11135b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
11145b54dac8SHaiyang Zhang 		goto out;
11155b54dac8SHaiyang Zhang 
1116e01ec219SKY Srinivasan 	num_rss_qs = min(device_info->max_num_vrss_chns, rsscap.num_recv_que);
1117e01ec219SKY Srinivasan 
111859995370SAndrew Schwartzmeyer 	net_device->max_chn = rsscap.num_recv_que;
1119e01ec219SKY Srinivasan 
1120e01ec219SKY Srinivasan 	/*
1121e01ec219SKY Srinivasan 	 * We will limit the VRSS channels to the number CPUs in the NUMA node
1122e01ec219SKY Srinivasan 	 * the primary channel is currently bound to.
1123e01ec219SKY Srinivasan 	 */
1124e01ec219SKY Srinivasan 	node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu));
1125e01ec219SKY Srinivasan 	num_possible_rss_qs = cpumask_weight(node_cpu_mask);
11268ebdcc52SAndrew Schwartzmeyer 
11278ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
11288ebdcc52SAndrew Schwartzmeyer 	if (device_info->num_chn && device_info->num_chn < net_device->max_chn)
11298ebdcc52SAndrew Schwartzmeyer 		net_device->num_chn = device_info->num_chn;
11308ebdcc52SAndrew Schwartzmeyer 	else
1131e01ec219SKY Srinivasan 		net_device->num_chn = min(num_possible_rss_qs, num_rss_qs);
1132e01ec219SKY Srinivasan 
1133b3e6b82aSKY Srinivasan 	num_rss_qs = net_device->num_chn - 1;
1134b3e6b82aSKY Srinivasan 	net_device->num_sc_offered = num_rss_qs;
1135b3e6b82aSKY Srinivasan 
11365b54dac8SHaiyang Zhang 	if (net_device->num_chn == 1)
11375b54dac8SHaiyang Zhang 		goto out;
11385b54dac8SHaiyang Zhang 
11395b54dac8SHaiyang Zhang 	net_device->sub_cb_buf = vzalloc((net_device->num_chn - 1) *
11405b54dac8SHaiyang Zhang 					 NETVSC_PACKET_SIZE);
11415b54dac8SHaiyang Zhang 	if (!net_device->sub_cb_buf) {
11425b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
11435b54dac8SHaiyang Zhang 		dev_info(&dev->device, "No memory for subchannels.\n");
11445b54dac8SHaiyang Zhang 		goto out;
11455b54dac8SHaiyang Zhang 	}
11465b54dac8SHaiyang Zhang 
11475b54dac8SHaiyang Zhang 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
11485b54dac8SHaiyang Zhang 
11495b54dac8SHaiyang Zhang 	init_packet = &net_device->channel_init_pkt;
11505b54dac8SHaiyang Zhang 	memset(init_packet, 0, sizeof(struct nvsp_message));
11515b54dac8SHaiyang Zhang 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
11525b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
11535b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
11545b54dac8SHaiyang Zhang 						net_device->num_chn - 1;
11555b54dac8SHaiyang Zhang 	ret = vmbus_sendpacket(dev->channel, init_packet,
11565b54dac8SHaiyang Zhang 			       sizeof(struct nvsp_message),
11575b54dac8SHaiyang Zhang 			       (unsigned long)init_packet,
11585b54dac8SHaiyang Zhang 			       VM_PKT_DATA_INBAND,
11595b54dac8SHaiyang Zhang 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
11605b54dac8SHaiyang Zhang 	if (ret)
11615b54dac8SHaiyang Zhang 		goto out;
11625b54dac8SHaiyang Zhang 	t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
11635b54dac8SHaiyang Zhang 	if (t == 0) {
11645b54dac8SHaiyang Zhang 		ret = -ETIMEDOUT;
11655b54dac8SHaiyang Zhang 		goto out;
11665b54dac8SHaiyang Zhang 	}
11675b54dac8SHaiyang Zhang 	if (init_packet->msg.v5_msg.subchn_comp.status !=
11685b54dac8SHaiyang Zhang 	    NVSP_STAT_SUCCESS) {
11695b54dac8SHaiyang Zhang 		ret = -ENODEV;
11705b54dac8SHaiyang Zhang 		goto out;
11715b54dac8SHaiyang Zhang 	}
11725b54dac8SHaiyang Zhang 	net_device->num_chn = 1 +
11735b54dac8SHaiyang Zhang 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
11745b54dac8SHaiyang Zhang 
11755b54dac8SHaiyang Zhang 	ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn);
11765b54dac8SHaiyang Zhang 
1177b3e6b82aSKY Srinivasan 	/*
1178d66ab514SHaiyang Zhang 	 * Set the number of sub-channels to be received.
1179b3e6b82aSKY Srinivasan 	 */
1180b3e6b82aSKY Srinivasan 	spin_lock_irqsave(&net_device->sc_lock, flags);
1181b3e6b82aSKY Srinivasan 	sc_delta = num_rss_qs - (net_device->num_chn - 1);
1182b3e6b82aSKY Srinivasan 	net_device->num_sc_offered -= sc_delta;
1183b3e6b82aSKY Srinivasan 	spin_unlock_irqrestore(&net_device->sc_lock, flags);
1184b3e6b82aSKY Srinivasan 
11855b54dac8SHaiyang Zhang out:
118659995370SAndrew Schwartzmeyer 	if (ret) {
118759995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
11885b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
1189d66ab514SHaiyang Zhang 		net_device->num_sc_offered = 0;
119059995370SAndrew Schwartzmeyer 	}
1191b3e6b82aSKY Srinivasan 
11925b54dac8SHaiyang Zhang 	return 0; /* return 0 because primary channel can be used alone */
11934a0e70aeSKY Srinivasan 
11944a0e70aeSKY Srinivasan err_dev_remv:
11954a0e70aeSKY Srinivasan 	rndis_filter_device_remove(dev);
11964a0e70aeSKY Srinivasan 	return ret;
119795fa0405SHaiyang Zhang }
119895fa0405SHaiyang Zhang 
119995fa0405SHaiyang Zhang void rndis_filter_device_remove(struct hv_device *dev)
120095fa0405SHaiyang Zhang {
120195fa0405SHaiyang Zhang 	struct netvsc_device *net_dev = hv_get_drvdata(dev);
120295fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1203d66ab514SHaiyang Zhang 	unsigned long t;
1204d66ab514SHaiyang Zhang 
1205d66ab514SHaiyang Zhang 	/* If not all subchannel offers are complete, wait for them until
1206d66ab514SHaiyang Zhang 	 * completion to avoid race.
1207d66ab514SHaiyang Zhang 	 */
1208d66ab514SHaiyang Zhang 	while (net_dev->num_sc_offered > 0) {
1209d66ab514SHaiyang Zhang 		t = wait_for_completion_timeout(&net_dev->channel_init_wait,
1210d66ab514SHaiyang Zhang 						10 * HZ);
1211d66ab514SHaiyang Zhang 		if (t == 0)
1212d66ab514SHaiyang Zhang 			WARN(1, "Netvsc: Waiting for sub-channel processing");
1213d66ab514SHaiyang Zhang 	}
121495fa0405SHaiyang Zhang 
121595fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
121695fa0405SHaiyang Zhang 	rndis_filter_halt_device(rndis_dev);
121795fa0405SHaiyang Zhang 
121895fa0405SHaiyang Zhang 	kfree(rndis_dev);
121995fa0405SHaiyang Zhang 	net_dev->extension = NULL;
122095fa0405SHaiyang Zhang 
122195fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
122295fa0405SHaiyang Zhang }
122395fa0405SHaiyang Zhang 
122495fa0405SHaiyang Zhang 
122595fa0405SHaiyang Zhang int rndis_filter_open(struct hv_device *dev)
122695fa0405SHaiyang Zhang {
122795fa0405SHaiyang Zhang 	struct netvsc_device *net_device = hv_get_drvdata(dev);
122895fa0405SHaiyang Zhang 
122995fa0405SHaiyang Zhang 	if (!net_device)
123095fa0405SHaiyang Zhang 		return -EINVAL;
123195fa0405SHaiyang Zhang 
123295fa0405SHaiyang Zhang 	return rndis_filter_open_device(net_device->extension);
123395fa0405SHaiyang Zhang }
123495fa0405SHaiyang Zhang 
123595fa0405SHaiyang Zhang int rndis_filter_close(struct hv_device *dev)
123695fa0405SHaiyang Zhang {
12375fccab3bSHaiyang Zhang 	struct netvsc_device *nvdev = hv_get_drvdata(dev);
123895fa0405SHaiyang Zhang 
12395fccab3bSHaiyang Zhang 	if (!nvdev)
124095fa0405SHaiyang Zhang 		return -EINVAL;
124195fa0405SHaiyang Zhang 
12425fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
124395fa0405SHaiyang Zhang }
1244