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>
3095fa0405SHaiyang Zhang 
3195fa0405SHaiyang Zhang #include "hyperv_net.h"
3295fa0405SHaiyang Zhang 
3395fa0405SHaiyang Zhang 
345b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
3595fa0405SHaiyang Zhang struct rndis_request {
3695fa0405SHaiyang Zhang 	struct list_head list_ent;
3795fa0405SHaiyang Zhang 	struct completion  wait_event;
3895fa0405SHaiyang Zhang 
3995fa0405SHaiyang Zhang 	struct rndis_message response_msg;
40a3a6cab5SHaiyang Zhang 	/*
41a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
42a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
43a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
44a3a6cab5SHaiyang Zhang 	 * future.
45a3a6cab5SHaiyang Zhang 	 */
46a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
4795fa0405SHaiyang Zhang 
4895fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
4995fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
5099e3fcfaSHaiyang Zhang 	/* Set 2 pages for rndis requests crossing page boundary */
5199e3fcfaSHaiyang Zhang 	struct hv_page_buffer buf[2];
520f48917bSHaiyang Zhang 
5395fa0405SHaiyang Zhang 	struct rndis_message request_msg;
540f48917bSHaiyang Zhang 	/*
55a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
56a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
570f48917bSHaiyang Zhang 	 */
58a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
5995fa0405SHaiyang Zhang };
6095fa0405SHaiyang Zhang 
6195fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
6295fa0405SHaiyang Zhang {
6395fa0405SHaiyang Zhang 	struct rndis_device *device;
6495fa0405SHaiyang Zhang 
6595fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
6695fa0405SHaiyang Zhang 	if (!device)
6795fa0405SHaiyang Zhang 		return NULL;
6895fa0405SHaiyang Zhang 
6995fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
7095fa0405SHaiyang Zhang 
7195fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
7295fa0405SHaiyang Zhang 
7395fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
7495fa0405SHaiyang Zhang 
7595fa0405SHaiyang Zhang 	return device;
7695fa0405SHaiyang Zhang }
7795fa0405SHaiyang Zhang 
7895fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
7995fa0405SHaiyang Zhang 					     u32 msg_type,
8095fa0405SHaiyang Zhang 					     u32 msg_len)
8195fa0405SHaiyang Zhang {
8295fa0405SHaiyang Zhang 	struct rndis_request *request;
8395fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
8495fa0405SHaiyang Zhang 	struct rndis_set_request *set;
8595fa0405SHaiyang Zhang 	unsigned long flags;
8695fa0405SHaiyang Zhang 
8795fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
8895fa0405SHaiyang Zhang 	if (!request)
8995fa0405SHaiyang Zhang 		return NULL;
9095fa0405SHaiyang Zhang 
9195fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
9295fa0405SHaiyang Zhang 
9395fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
9495fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
9595fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
9695fa0405SHaiyang Zhang 
975b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
985b54dac8SHaiyang Zhang 
9995fa0405SHaiyang Zhang 	/*
10095fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
10195fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
10295fa0405SHaiyang Zhang 	 * template
10395fa0405SHaiyang Zhang 	 */
10495fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
10595fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
10695fa0405SHaiyang Zhang 
10795fa0405SHaiyang Zhang 	/* Add to the request list */
10895fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
10995fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
11095fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
11195fa0405SHaiyang Zhang 
11295fa0405SHaiyang Zhang 	return request;
11395fa0405SHaiyang Zhang }
11495fa0405SHaiyang Zhang 
11595fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
11695fa0405SHaiyang Zhang 			    struct rndis_request *req)
11795fa0405SHaiyang Zhang {
11895fa0405SHaiyang Zhang 	unsigned long flags;
11995fa0405SHaiyang Zhang 
12095fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12195fa0405SHaiyang Zhang 	list_del(&req->list_ent);
12295fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12395fa0405SHaiyang Zhang 
12495fa0405SHaiyang Zhang 	kfree(req);
12595fa0405SHaiyang Zhang }
12695fa0405SHaiyang Zhang 
12795fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev,
12895fa0405SHaiyang Zhang 			struct rndis_message *rndis_msg)
12995fa0405SHaiyang Zhang {
13095fa0405SHaiyang Zhang 	struct net_device *netdev;
13195fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
13295fa0405SHaiyang Zhang 
13395fa0405SHaiyang Zhang 	net_device = hv_get_drvdata(hv_dev);
13495fa0405SHaiyang Zhang 	netdev = net_device->ndev;
13595fa0405SHaiyang Zhang 
13695fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
13751491167SLinus Walleij 	case RNDIS_MSG_PACKET:
13851491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
13995fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14095fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14195fa0405SHaiyang Zhang 			   "pkt len %u\n",
14295fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14395fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
14495fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
14595fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
14695fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
14795fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
14895fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15095fa0405SHaiyang Zhang 		break;
15195fa0405SHaiyang Zhang 
15251491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15351491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
15495fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
15595fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
15695fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
15795fa0405SHaiyang Zhang 			rndis_msg->msg_len,
15895fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
15995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16095fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16195fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16395fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
16595fa0405SHaiyang Zhang 			   max_pkt_per_msg,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
16795fa0405SHaiyang Zhang 			   pkt_alignment_factor);
16895fa0405SHaiyang Zhang 		break;
16995fa0405SHaiyang Zhang 
17051491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17151491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17295fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17395fa0405SHaiyang Zhang 			"buf offset %u)\n",
17495fa0405SHaiyang Zhang 			rndis_msg->msg_len,
17595fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
17695fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
17795fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
17895fa0405SHaiyang Zhang 			   info_buflen,
17995fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18095fa0405SHaiyang Zhang 			   info_buf_offset);
18195fa0405SHaiyang Zhang 		break;
18295fa0405SHaiyang Zhang 
18351491167SLinus Walleij 	case RNDIS_MSG_SET_C:
18495fa0405SHaiyang Zhang 		netdev_dbg(netdev,
18551491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
18695fa0405SHaiyang Zhang 			rndis_msg->msg_len,
18795fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
18895fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
18995fa0405SHaiyang Zhang 		break;
19095fa0405SHaiyang Zhang 
19151491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19251491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19395fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
19495fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19595fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
19695fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
19795fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
19895fa0405SHaiyang Zhang 		break;
19995fa0405SHaiyang Zhang 
20095fa0405SHaiyang Zhang 	default:
20195fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20295fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20395fa0405SHaiyang Zhang 			rndis_msg->msg_len);
20495fa0405SHaiyang Zhang 		break;
20595fa0405SHaiyang Zhang 	}
20695fa0405SHaiyang Zhang }
20795fa0405SHaiyang Zhang 
20895fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
20995fa0405SHaiyang Zhang 				  struct rndis_request *req)
21095fa0405SHaiyang Zhang {
21195fa0405SHaiyang Zhang 	int ret;
21295fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
21395fa0405SHaiyang Zhang 
21495fa0405SHaiyang Zhang 	/* Setup the packet to send it */
21595fa0405SHaiyang Zhang 	packet = &req->pkt;
21695fa0405SHaiyang Zhang 
21795fa0405SHaiyang Zhang 	packet->is_data_pkt = false;
21895fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
21995fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22095fa0405SHaiyang Zhang 
22195fa0405SHaiyang Zhang 	packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >>
22295fa0405SHaiyang Zhang 					PAGE_SHIFT;
22395fa0405SHaiyang Zhang 	packet->page_buf[0].len = req->request_msg.msg_len;
22495fa0405SHaiyang Zhang 	packet->page_buf[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 */
22899e3fcfaSHaiyang Zhang 	if (packet->page_buf[0].offset + packet->page_buf[0].len > PAGE_SIZE) {
22999e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
23099e3fcfaSHaiyang Zhang 		packet->page_buf[0].len = PAGE_SIZE -
23199e3fcfaSHaiyang Zhang 			packet->page_buf[0].offset;
23299e3fcfaSHaiyang Zhang 		packet->page_buf[1].pfn = virt_to_phys((void *)&req->request_msg
23399e3fcfaSHaiyang Zhang 			+ packet->page_buf[0].len) >> PAGE_SHIFT;
23499e3fcfaSHaiyang Zhang 		packet->page_buf[1].offset = 0;
23599e3fcfaSHaiyang Zhang 		packet->page_buf[1].len = req->request_msg.msg_len -
23699e3fcfaSHaiyang Zhang 			packet->page_buf[0].len;
23799e3fcfaSHaiyang Zhang 	}
23899e3fcfaSHaiyang Zhang 
239893f6627SHaiyang Zhang 	packet->send_completion = NULL;
24095fa0405SHaiyang Zhang 
24195fa0405SHaiyang Zhang 	ret = netvsc_send(dev->net_dev->dev, packet);
24295fa0405SHaiyang Zhang 	return ret;
24395fa0405SHaiyang Zhang }
24495fa0405SHaiyang Zhang 
2451b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2461b07da51SHaiyang Zhang 				 struct rndis_request *request)
2471b07da51SHaiyang Zhang {
2481b07da51SHaiyang Zhang 	u32 link_status;
2491b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2501b07da51SHaiyang Zhang 
2511b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2521b07da51SHaiyang Zhang 
2531b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2541b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2551b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2561b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2571b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2581b07da51SHaiyang Zhang 	}
2591b07da51SHaiyang Zhang }
2601b07da51SHaiyang Zhang 
26195fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev,
26295fa0405SHaiyang Zhang 				       struct rndis_message *resp)
26395fa0405SHaiyang Zhang {
26495fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
26595fa0405SHaiyang Zhang 	bool found = false;
26695fa0405SHaiyang Zhang 	unsigned long flags;
26795fa0405SHaiyang Zhang 	struct net_device *ndev;
26895fa0405SHaiyang Zhang 
26995fa0405SHaiyang Zhang 	ndev = dev->net_dev->ndev;
27095fa0405SHaiyang Zhang 
27195fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
27295fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
27395fa0405SHaiyang Zhang 		/*
27495fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
27595fa0405SHaiyang Zhang 		 * field
27695fa0405SHaiyang Zhang 		 */
27795fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
27895fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
27995fa0405SHaiyang Zhang 			found = true;
28095fa0405SHaiyang Zhang 			break;
28195fa0405SHaiyang Zhang 		}
28295fa0405SHaiyang Zhang 	}
28395fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
28495fa0405SHaiyang Zhang 
28595fa0405SHaiyang Zhang 	if (found) {
286a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
287a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
28895fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
28995fa0405SHaiyang Zhang 			       resp->msg_len);
2901b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
2911b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
2921b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
2931b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
29495fa0405SHaiyang Zhang 		} else {
29595fa0405SHaiyang Zhang 			netdev_err(ndev,
29695fa0405SHaiyang Zhang 				"rndis response buffer overflow "
29795fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
29895fa0405SHaiyang Zhang 				resp->msg_len,
29986eedaccSKY Srinivasan 				sizeof(struct rndis_message));
30095fa0405SHaiyang Zhang 
30195fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
30251491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
30395fa0405SHaiyang Zhang 				/* does not have a request id field */
30495fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
305007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
30695fa0405SHaiyang Zhang 			} else {
30795fa0405SHaiyang Zhang 				request->response_msg.msg.
30895fa0405SHaiyang Zhang 				init_complete.status =
309007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
31095fa0405SHaiyang Zhang 			}
31195fa0405SHaiyang Zhang 		}
31295fa0405SHaiyang Zhang 
31395fa0405SHaiyang Zhang 		complete(&request->wait_event);
31495fa0405SHaiyang Zhang 	} else {
31595fa0405SHaiyang Zhang 		netdev_err(ndev,
31695fa0405SHaiyang Zhang 			"no rndis request found for this response "
31795fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
31895fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
31995fa0405SHaiyang Zhang 			resp->ndis_msg_type);
32095fa0405SHaiyang Zhang 	}
32195fa0405SHaiyang Zhang }
32295fa0405SHaiyang Zhang 
3231f5f3a75SHaiyang Zhang /*
3241f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3251f5f3a75SHaiyang Zhang  * return NULL if not found.
3261f5f3a75SHaiyang Zhang  */
3271f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3281f5f3a75SHaiyang Zhang {
3291f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3301f5f3a75SHaiyang Zhang 	int len;
3311f5f3a75SHaiyang Zhang 
3321f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3331f5f3a75SHaiyang Zhang 		return NULL;
3341f5f3a75SHaiyang Zhang 
3351f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3361f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3371f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3381f5f3a75SHaiyang Zhang 
3391f5f3a75SHaiyang Zhang 	while (len > 0) {
3401f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3411f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3421f5f3a75SHaiyang Zhang 		len -= ppi->size;
3431f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3441f5f3a75SHaiyang Zhang 	}
3451f5f3a75SHaiyang Zhang 
3461f5f3a75SHaiyang Zhang 	return NULL;
3471f5f3a75SHaiyang Zhang }
3481f5f3a75SHaiyang Zhang 
34995fa0405SHaiyang Zhang static void rndis_filter_receive_data(struct rndis_device *dev,
35095fa0405SHaiyang Zhang 				   struct rndis_message *msg,
35195fa0405SHaiyang Zhang 				   struct hv_netvsc_packet *pkt)
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;
35795fa0405SHaiyang Zhang 
35895fa0405SHaiyang Zhang 	rndis_pkt = &msg->msg.pkt;
35995fa0405SHaiyang Zhang 
36095fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
36195fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
36295fa0405SHaiyang Zhang 
36395fa0405SHaiyang Zhang 	pkt->total_data_buflen -= data_offset;
3644b8a8bc9SWei Yongjun 
3654b8a8bc9SWei Yongjun 	/*
3664b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3674b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3684b8a8bc9SWei Yongjun 	 */
3694b8a8bc9SWei Yongjun 	if (pkt->total_data_buflen < rndis_pkt->data_len) {
3704b8a8bc9SWei Yongjun 		netdev_err(dev->net_dev->ndev, "rndis message buffer "
3714b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3724b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
3734b8a8bc9SWei Yongjun 			   pkt->total_data_buflen, rndis_pkt->data_len);
3744b8a8bc9SWei Yongjun 		return;
3754b8a8bc9SWei Yongjun 	}
3764b8a8bc9SWei Yongjun 
3774b8a8bc9SWei Yongjun 	/*
3784b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
3794b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
3804b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
3814b8a8bc9SWei Yongjun 	 */
3824b8a8bc9SWei Yongjun 	pkt->total_data_buflen = rndis_pkt->data_len;
38345326342SHaiyang Zhang 	pkt->data = (void *)((unsigned long)pkt->data + data_offset);
38495fa0405SHaiyang Zhang 
3851f5f3a75SHaiyang Zhang 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
3861f5f3a75SHaiyang Zhang 	if (vlan) {
3871f5f3a75SHaiyang Zhang 		pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
3881f5f3a75SHaiyang Zhang 			(vlan->pri << VLAN_PRIO_SHIFT);
3891f5f3a75SHaiyang Zhang 	} else {
3901f5f3a75SHaiyang Zhang 		pkt->vlan_tci = 0;
3911f5f3a75SHaiyang Zhang 	}
3921f5f3a75SHaiyang Zhang 
393e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
394e3d605edSKY Srinivasan 	netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info);
39595fa0405SHaiyang Zhang }
39695fa0405SHaiyang Zhang 
39795fa0405SHaiyang Zhang int rndis_filter_receive(struct hv_device *dev,
39895fa0405SHaiyang Zhang 				struct hv_netvsc_packet	*pkt)
39995fa0405SHaiyang Zhang {
40095fa0405SHaiyang Zhang 	struct netvsc_device *net_dev = hv_get_drvdata(dev);
40195fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev;
402ef31bef6SHaiyang Zhang 	struct rndis_message *rndis_msg;
40395fa0405SHaiyang Zhang 	struct net_device *ndev;
40463f6921dSHaiyang Zhang 	int ret = 0;
40595fa0405SHaiyang Zhang 
40663f6921dSHaiyang Zhang 	if (!net_dev) {
40763f6921dSHaiyang Zhang 		ret = -EINVAL;
40863f6921dSHaiyang Zhang 		goto exit;
40963f6921dSHaiyang Zhang 	}
41095fa0405SHaiyang Zhang 
41195fa0405SHaiyang Zhang 	ndev = net_dev->ndev;
41295fa0405SHaiyang Zhang 
41395fa0405SHaiyang Zhang 	/* Make sure the rndis device state is initialized */
41495fa0405SHaiyang Zhang 	if (!net_dev->extension) {
41595fa0405SHaiyang Zhang 		netdev_err(ndev, "got rndis message but no rndis device - "
41695fa0405SHaiyang Zhang 			  "dropping this message!\n");
41763f6921dSHaiyang Zhang 		ret = -ENODEV;
41863f6921dSHaiyang Zhang 		goto exit;
41995fa0405SHaiyang Zhang 	}
42095fa0405SHaiyang Zhang 
42195fa0405SHaiyang Zhang 	rndis_dev = (struct rndis_device *)net_dev->extension;
42295fa0405SHaiyang Zhang 	if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
42395fa0405SHaiyang Zhang 		netdev_err(ndev, "got rndis message but rndis device "
42495fa0405SHaiyang Zhang 			   "uninitialized...dropping this message!\n");
42563f6921dSHaiyang Zhang 		ret = -ENODEV;
42663f6921dSHaiyang Zhang 		goto exit;
42795fa0405SHaiyang Zhang 	}
42895fa0405SHaiyang Zhang 
429ef31bef6SHaiyang Zhang 	rndis_msg = pkt->data;
43095fa0405SHaiyang Zhang 
431ef31bef6SHaiyang Zhang 	dump_rndis_message(dev, rndis_msg);
43295fa0405SHaiyang Zhang 
433ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
43451491167SLinus Walleij 	case RNDIS_MSG_PACKET:
43595fa0405SHaiyang Zhang 		/* data msg */
436ef31bef6SHaiyang Zhang 		rndis_filter_receive_data(rndis_dev, rndis_msg, pkt);
43795fa0405SHaiyang Zhang 		break;
43895fa0405SHaiyang Zhang 
43951491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
44051491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
44151491167SLinus Walleij 	case RNDIS_MSG_SET_C:
44295fa0405SHaiyang Zhang 		/* completion msgs */
443ef31bef6SHaiyang Zhang 		rndis_filter_receive_response(rndis_dev, rndis_msg);
44495fa0405SHaiyang Zhang 		break;
44595fa0405SHaiyang Zhang 
44651491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
44795fa0405SHaiyang Zhang 		/* notification msgs */
4483a494e71SHaiyang Zhang 		netvsc_linkstatus_callback(dev, rndis_msg);
44995fa0405SHaiyang Zhang 		break;
45095fa0405SHaiyang Zhang 	default:
45195fa0405SHaiyang Zhang 		netdev_err(ndev,
45295fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
453ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
454ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
45595fa0405SHaiyang Zhang 		break;
45695fa0405SHaiyang Zhang 	}
45795fa0405SHaiyang Zhang 
45863f6921dSHaiyang Zhang exit:
45963f6921dSHaiyang Zhang 	if (ret != 0)
46063f6921dSHaiyang Zhang 		pkt->status = NVSP_STAT_FAIL;
46163f6921dSHaiyang Zhang 
46263f6921dSHaiyang Zhang 	return ret;
46395fa0405SHaiyang Zhang }
46495fa0405SHaiyang Zhang 
46595fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
46695fa0405SHaiyang Zhang 				  void *result, u32 *result_size)
46795fa0405SHaiyang Zhang {
46895fa0405SHaiyang Zhang 	struct rndis_request *request;
46995fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
47095fa0405SHaiyang Zhang 	struct rndis_query_request *query;
47195fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
47295fa0405SHaiyang Zhang 	int ret = 0;
47395fa0405SHaiyang Zhang 	int t;
47495fa0405SHaiyang Zhang 
47595fa0405SHaiyang Zhang 	if (!result)
47695fa0405SHaiyang Zhang 		return -EINVAL;
47795fa0405SHaiyang Zhang 
47895fa0405SHaiyang Zhang 	*result_size = 0;
47951491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
48095fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
48195fa0405SHaiyang Zhang 	if (!request) {
48295fa0405SHaiyang Zhang 		ret = -ENOMEM;
48395fa0405SHaiyang Zhang 		goto cleanup;
48495fa0405SHaiyang Zhang 	}
48595fa0405SHaiyang Zhang 
48695fa0405SHaiyang Zhang 	/* Setup the rndis query */
48795fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
48895fa0405SHaiyang Zhang 	query->oid = oid;
48995fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
49095fa0405SHaiyang Zhang 	query->info_buflen = 0;
49195fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
49295fa0405SHaiyang Zhang 
4935b54dac8SHaiyang Zhang 	if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
4945b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
4955b54dac8SHaiyang Zhang 
4965b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
4975b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
4985b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
4995b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5005b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5015b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5025b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5035b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5045b54dac8SHaiyang Zhang 	}
5055b54dac8SHaiyang Zhang 
50695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
50795fa0405SHaiyang Zhang 	if (ret != 0)
50895fa0405SHaiyang Zhang 		goto cleanup;
50995fa0405SHaiyang Zhang 
51095fa0405SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
51195fa0405SHaiyang Zhang 	if (t == 0) {
51295fa0405SHaiyang Zhang 		ret = -ETIMEDOUT;
51395fa0405SHaiyang Zhang 		goto cleanup;
51495fa0405SHaiyang Zhang 	}
51595fa0405SHaiyang Zhang 
51695fa0405SHaiyang Zhang 	/* Copy the response back */
51795fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
51895fa0405SHaiyang Zhang 
51995fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
52095fa0405SHaiyang Zhang 		ret = -1;
52195fa0405SHaiyang Zhang 		goto cleanup;
52295fa0405SHaiyang Zhang 	}
52395fa0405SHaiyang Zhang 
52495fa0405SHaiyang Zhang 	memcpy(result,
52595fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
52695fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
52795fa0405SHaiyang Zhang 	       query_complete->info_buflen);
52895fa0405SHaiyang Zhang 
52995fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
53095fa0405SHaiyang Zhang 
53195fa0405SHaiyang Zhang cleanup:
53295fa0405SHaiyang Zhang 	if (request)
53395fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
53495fa0405SHaiyang Zhang 
53595fa0405SHaiyang Zhang 	return ret;
53695fa0405SHaiyang Zhang }
53795fa0405SHaiyang Zhang 
53895fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev)
53995fa0405SHaiyang Zhang {
54095fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
54195fa0405SHaiyang Zhang 
54295fa0405SHaiyang Zhang 	return rndis_filter_query_device(dev,
54395fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
54495fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
54595fa0405SHaiyang Zhang }
54695fa0405SHaiyang Zhang 
5471ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5481ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5491ce09e89SHaiyang Zhang 
5501ce09e89SHaiyang Zhang int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac)
5511ce09e89SHaiyang Zhang {
5521ce09e89SHaiyang Zhang 	struct netvsc_device *nvdev = hv_get_drvdata(hdev);
5531ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
5541ce09e89SHaiyang Zhang 	struct net_device *ndev = nvdev->ndev;
5551ce09e89SHaiyang Zhang 	struct rndis_request *request;
5561ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
5571ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
5581ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
5591ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
5601ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
5611ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
5621ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
5631ce09e89SHaiyang Zhang 	int ret, t;
5641ce09e89SHaiyang Zhang 
5651ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
5661ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
5671ce09e89SHaiyang Zhang 	if (!request)
5681ce09e89SHaiyang Zhang 		return -ENOMEM;
5691ce09e89SHaiyang Zhang 
5701ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
5711ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
5721ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
5731ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
5741ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
5751ce09e89SHaiyang Zhang 
5761ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
5771ce09e89SHaiyang Zhang 		set->info_buf_offset);
5781ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
5791ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
5801ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
5811ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
5821ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
5831ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
5841ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
5851ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
5861ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
5871ce09e89SHaiyang Zhang 
5881ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
5891ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
5901ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
5911ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
5921ce09e89SHaiyang Zhang 	if (ret < 0)
5931ce09e89SHaiyang Zhang 		goto cleanup;
5941ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
5951ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
5961ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
5971ce09e89SHaiyang Zhang 	if (ret < 0)
5981ce09e89SHaiyang Zhang 		goto cleanup;
5991ce09e89SHaiyang Zhang 
6001ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6011ce09e89SHaiyang Zhang 	if (ret != 0)
6021ce09e89SHaiyang Zhang 		goto cleanup;
6031ce09e89SHaiyang Zhang 
6041ce09e89SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
6051ce09e89SHaiyang Zhang 	if (t == 0) {
6061ce09e89SHaiyang Zhang 		netdev_err(ndev, "timeout before we got a set response...\n");
6071ce09e89SHaiyang Zhang 		/*
6081ce09e89SHaiyang Zhang 		 * can't put_rndis_request, since we may still receive a
6091ce09e89SHaiyang Zhang 		 * send-completion.
6101ce09e89SHaiyang Zhang 		 */
6111ce09e89SHaiyang Zhang 		return -EBUSY;
6121ce09e89SHaiyang Zhang 	} else {
6131ce09e89SHaiyang Zhang 		set_complete = &request->response_msg.msg.set_complete;
614b02a8067SHaiyang Zhang 		if (set_complete->status != RNDIS_STATUS_SUCCESS) {
615b02a8067SHaiyang Zhang 			netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
616b02a8067SHaiyang Zhang 				   set_complete->status);
6171ce09e89SHaiyang Zhang 			ret = -EINVAL;
6181ce09e89SHaiyang Zhang 		}
619b02a8067SHaiyang Zhang 	}
6201ce09e89SHaiyang Zhang 
6211ce09e89SHaiyang Zhang cleanup:
6221ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6231ce09e89SHaiyang Zhang 	return ret;
6241ce09e89SHaiyang Zhang }
6251ce09e89SHaiyang Zhang 
6264a0e70aeSKY Srinivasan int rndis_filter_set_offload_params(struct hv_device *hdev,
6274a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6284a0e70aeSKY Srinivasan {
6294a0e70aeSKY Srinivasan 	struct netvsc_device *nvdev = hv_get_drvdata(hdev);
6304a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6314a0e70aeSKY Srinivasan 	struct net_device *ndev = nvdev->ndev;
6324a0e70aeSKY Srinivasan 	struct rndis_request *request;
6334a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6344a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6354a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6364a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
6374a0e70aeSKY Srinivasan 	int ret, t;
638af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
639af9893a3SKY Srinivasan 
640af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
641af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
642af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
643af9893a3SKY Srinivasan 		 * UDP checksum offload.
644af9893a3SKY Srinivasan 		 */
645af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
646af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
647af9893a3SKY Srinivasan 	}
6484a0e70aeSKY Srinivasan 
6494a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6504a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6514a0e70aeSKY Srinivasan 	if (!request)
6524a0e70aeSKY Srinivasan 		return -ENOMEM;
6534a0e70aeSKY Srinivasan 
6544a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6554a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6564a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6574a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6584a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
6594a0e70aeSKY Srinivasan 
6604a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
6614a0e70aeSKY Srinivasan 				set->info_buf_offset);
6624a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
6634a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
6644a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
6654a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
6664a0e70aeSKY Srinivasan 
6674a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
6684a0e70aeSKY Srinivasan 	if (ret != 0)
6694a0e70aeSKY Srinivasan 		goto cleanup;
6704a0e70aeSKY Srinivasan 
6714a0e70aeSKY Srinivasan 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
6724a0e70aeSKY Srinivasan 	if (t == 0) {
6734a0e70aeSKY Srinivasan 		netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n");
6744a0e70aeSKY Srinivasan 		/* can't put_rndis_request, since we may still receive a
6754a0e70aeSKY Srinivasan 		 * send-completion.
6764a0e70aeSKY Srinivasan 		 */
6774a0e70aeSKY Srinivasan 		return -EBUSY;
6784a0e70aeSKY Srinivasan 	} else {
6794a0e70aeSKY Srinivasan 		set_complete = &request->response_msg.msg.set_complete;
6804a0e70aeSKY Srinivasan 		if (set_complete->status != RNDIS_STATUS_SUCCESS) {
681af9893a3SKY Srinivasan 			netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
6824a0e70aeSKY Srinivasan 				   set_complete->status);
6834a0e70aeSKY Srinivasan 			ret = -EINVAL;
6844a0e70aeSKY Srinivasan 		}
6854a0e70aeSKY Srinivasan 	}
6864a0e70aeSKY Srinivasan 
6874a0e70aeSKY Srinivasan cleanup:
6884a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
6894a0e70aeSKY Srinivasan 	return ret;
6904a0e70aeSKY Srinivasan }
6911ce09e89SHaiyang Zhang 
6925b54dac8SHaiyang Zhang u8 netvsc_hash_key[HASH_KEYLEN] = {
6935b54dac8SHaiyang Zhang 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
6945b54dac8SHaiyang Zhang 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
6955b54dac8SHaiyang Zhang 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
6965b54dac8SHaiyang Zhang 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
6975b54dac8SHaiyang Zhang 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
6985b54dac8SHaiyang Zhang };
6995b54dac8SHaiyang Zhang 
7005b54dac8SHaiyang Zhang int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
7015b54dac8SHaiyang Zhang {
7025b54dac8SHaiyang Zhang 	struct net_device *ndev = rdev->net_dev->ndev;
7035b54dac8SHaiyang Zhang 	struct rndis_request *request;
7045b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7055b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7065b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
7075b54dac8SHaiyang Zhang 		     4*ITAB_NUM + HASH_KEYLEN;
7085b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7095b54dac8SHaiyang Zhang 	u32 *itab;
7105b54dac8SHaiyang Zhang 	u8 *keyp;
7115b54dac8SHaiyang Zhang 	int i, t, ret;
7125b54dac8SHaiyang Zhang 
7135b54dac8SHaiyang Zhang 	request = get_rndis_request(
7145b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7155b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7165b54dac8SHaiyang Zhang 	if (!request)
7175b54dac8SHaiyang Zhang 		return -ENOMEM;
7185b54dac8SHaiyang Zhang 
7195b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7205b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7215b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7225b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7235b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7245b54dac8SHaiyang Zhang 
7255b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7265b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7275b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7285b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7295b54dac8SHaiyang Zhang 	rssp->flag = 0;
7305b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7315b54dac8SHaiyang Zhang 			 NDIS_HASH_TCP_IPV4;
7325b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7335b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
7345b54dac8SHaiyang Zhang 	rssp->hashkey_size = HASH_KEYLEN;
7355b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7365b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7375b54dac8SHaiyang Zhang 
7385b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7395b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7405b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
7415b54dac8SHaiyang Zhang 		itab[i] = i % num_queue;
7425b54dac8SHaiyang Zhang 
7435b54dac8SHaiyang Zhang 	/* Set hask key values */
7445b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
7455b54dac8SHaiyang Zhang 	for (i = 0; i < HASH_KEYLEN; i++)
7465b54dac8SHaiyang Zhang 		keyp[i] = netvsc_hash_key[i];
7475b54dac8SHaiyang Zhang 
7485b54dac8SHaiyang Zhang 
7495b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7505b54dac8SHaiyang Zhang 	if (ret != 0)
7515b54dac8SHaiyang Zhang 		goto cleanup;
7525b54dac8SHaiyang Zhang 
7535b54dac8SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
7545b54dac8SHaiyang Zhang 	if (t == 0) {
7555b54dac8SHaiyang Zhang 		netdev_err(ndev, "timeout before we got a set response...\n");
7565b54dac8SHaiyang Zhang 		/* can't put_rndis_request, since we may still receive a
7575b54dac8SHaiyang Zhang 		 * send-completion.
7585b54dac8SHaiyang Zhang 		 */
7595b54dac8SHaiyang Zhang 		return -ETIMEDOUT;
7605b54dac8SHaiyang Zhang 	} else {
7615b54dac8SHaiyang Zhang 		set_complete = &request->response_msg.msg.set_complete;
7625b54dac8SHaiyang Zhang 		if (set_complete->status != RNDIS_STATUS_SUCCESS) {
7635b54dac8SHaiyang Zhang 			netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7645b54dac8SHaiyang Zhang 				   set_complete->status);
7655b54dac8SHaiyang Zhang 			ret = -EINVAL;
7665b54dac8SHaiyang Zhang 		}
7675b54dac8SHaiyang Zhang 	}
7685b54dac8SHaiyang Zhang 
7695b54dac8SHaiyang Zhang cleanup:
7705b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7715b54dac8SHaiyang Zhang 	return ret;
7725b54dac8SHaiyang Zhang }
7735b54dac8SHaiyang Zhang 
7745b54dac8SHaiyang Zhang 
77595fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev)
77695fa0405SHaiyang Zhang {
77795fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
77895fa0405SHaiyang Zhang 	u32 link_status;
77995fa0405SHaiyang Zhang 	int ret;
78095fa0405SHaiyang Zhang 
78195fa0405SHaiyang Zhang 	ret = rndis_filter_query_device(dev,
78295fa0405SHaiyang Zhang 				      RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
78395fa0405SHaiyang Zhang 				      &link_status, &size);
78495fa0405SHaiyang Zhang 
78595fa0405SHaiyang Zhang 	return ret;
78695fa0405SHaiyang Zhang }
78795fa0405SHaiyang Zhang 
788d426b2e3SHaiyang Zhang int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter)
78995fa0405SHaiyang Zhang {
79095fa0405SHaiyang Zhang 	struct rndis_request *request;
79195fa0405SHaiyang Zhang 	struct rndis_set_request *set;
79295fa0405SHaiyang Zhang 	struct rndis_set_complete *set_complete;
79395fa0405SHaiyang Zhang 	u32 status;
79495fa0405SHaiyang Zhang 	int ret, t;
79595fa0405SHaiyang Zhang 	struct net_device *ndev;
79695fa0405SHaiyang Zhang 
79795fa0405SHaiyang Zhang 	ndev = dev->net_dev->ndev;
79895fa0405SHaiyang Zhang 
79951491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
80095fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
80195fa0405SHaiyang Zhang 			sizeof(u32));
80295fa0405SHaiyang Zhang 	if (!request) {
80395fa0405SHaiyang Zhang 		ret = -ENOMEM;
80495fa0405SHaiyang Zhang 		goto cleanup;
80595fa0405SHaiyang Zhang 	}
80695fa0405SHaiyang Zhang 
80795fa0405SHaiyang Zhang 	/* Setup the rndis set */
80895fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
80995fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
81095fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
81195fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
81295fa0405SHaiyang Zhang 
81395fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
81495fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
81595fa0405SHaiyang Zhang 
81695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
81795fa0405SHaiyang Zhang 	if (ret != 0)
81895fa0405SHaiyang Zhang 		goto cleanup;
81995fa0405SHaiyang Zhang 
82095fa0405SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
82195fa0405SHaiyang Zhang 
82295fa0405SHaiyang Zhang 	if (t == 0) {
82395fa0405SHaiyang Zhang 		netdev_err(ndev,
82495fa0405SHaiyang Zhang 			"timeout before we got a set response...\n");
825ea496374SHaiyang Zhang 		ret = -ETIMEDOUT;
82695fa0405SHaiyang Zhang 		/*
82795fa0405SHaiyang Zhang 		 * We can't deallocate the request since we may still receive a
82895fa0405SHaiyang Zhang 		 * send completion for it.
82995fa0405SHaiyang Zhang 		 */
83095fa0405SHaiyang Zhang 		goto exit;
83195fa0405SHaiyang Zhang 	} else {
83295fa0405SHaiyang Zhang 		set_complete = &request->response_msg.msg.set_complete;
83395fa0405SHaiyang Zhang 		status = set_complete->status;
83495fa0405SHaiyang Zhang 	}
83595fa0405SHaiyang Zhang 
83695fa0405SHaiyang Zhang cleanup:
83795fa0405SHaiyang Zhang 	if (request)
83895fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
83995fa0405SHaiyang Zhang exit:
84095fa0405SHaiyang Zhang 	return ret;
84195fa0405SHaiyang Zhang }
84295fa0405SHaiyang Zhang 
84395fa0405SHaiyang Zhang 
84495fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev)
84595fa0405SHaiyang Zhang {
84695fa0405SHaiyang Zhang 	struct rndis_request *request;
84795fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
84895fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
84995fa0405SHaiyang Zhang 	u32 status;
85095fa0405SHaiyang Zhang 	int ret, t;
85195fa0405SHaiyang Zhang 
85251491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
85395fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
85495fa0405SHaiyang Zhang 	if (!request) {
85595fa0405SHaiyang Zhang 		ret = -ENOMEM;
85695fa0405SHaiyang Zhang 		goto cleanup;
85795fa0405SHaiyang Zhang 	}
85895fa0405SHaiyang Zhang 
85995fa0405SHaiyang Zhang 	/* Setup the rndis set */
86095fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
86195fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
86295fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
863fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
86495fa0405SHaiyang Zhang 
86595fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
86695fa0405SHaiyang Zhang 
86795fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
86895fa0405SHaiyang Zhang 	if (ret != 0) {
86995fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
87095fa0405SHaiyang Zhang 		goto cleanup;
87195fa0405SHaiyang Zhang 	}
87295fa0405SHaiyang Zhang 
87395fa0405SHaiyang Zhang 
87495fa0405SHaiyang Zhang 	t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
87595fa0405SHaiyang Zhang 
87695fa0405SHaiyang Zhang 	if (t == 0) {
87795fa0405SHaiyang Zhang 		ret = -ETIMEDOUT;
87895fa0405SHaiyang Zhang 		goto cleanup;
87995fa0405SHaiyang Zhang 	}
88095fa0405SHaiyang Zhang 
88195fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
88295fa0405SHaiyang Zhang 	status = init_complete->status;
88395fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
88495fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
88595fa0405SHaiyang Zhang 		ret = 0;
88695fa0405SHaiyang Zhang 	} else {
88795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
88895fa0405SHaiyang Zhang 		ret = -EINVAL;
88995fa0405SHaiyang Zhang 	}
89095fa0405SHaiyang Zhang 
89195fa0405SHaiyang Zhang cleanup:
89295fa0405SHaiyang Zhang 	if (request)
89395fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
89495fa0405SHaiyang Zhang 
89595fa0405SHaiyang Zhang 	return ret;
89695fa0405SHaiyang Zhang }
89795fa0405SHaiyang Zhang 
89895fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
89995fa0405SHaiyang Zhang {
90095fa0405SHaiyang Zhang 	struct rndis_request *request;
90195fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
902ae9e63bbSHaiyang Zhang 	struct netvsc_device *nvdev = dev->net_dev;
903ae9e63bbSHaiyang Zhang 	struct hv_device *hdev = nvdev->dev;
904ae9e63bbSHaiyang Zhang 	ulong flags;
90595fa0405SHaiyang Zhang 
90695fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
90751491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
90895fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
90995fa0405SHaiyang Zhang 	if (!request)
91095fa0405SHaiyang Zhang 		goto cleanup;
91195fa0405SHaiyang Zhang 
91295fa0405SHaiyang Zhang 	/* Setup the rndis set */
91395fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
91495fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
91595fa0405SHaiyang Zhang 
91695fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
91795fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
91895fa0405SHaiyang Zhang 
91995fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
92095fa0405SHaiyang Zhang 
92195fa0405SHaiyang Zhang cleanup:
922ae9e63bbSHaiyang Zhang 	spin_lock_irqsave(&hdev->channel->inbound_lock, flags);
923ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
924ae9e63bbSHaiyang Zhang 	spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
925ae9e63bbSHaiyang Zhang 
926ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
927ae9e63bbSHaiyang Zhang 	wait_event(nvdev->wait_drain,
928ae9e63bbSHaiyang Zhang 		atomic_read(&nvdev->num_outstanding_sends) == 0);
929ae9e63bbSHaiyang Zhang 
93095fa0405SHaiyang Zhang 	if (request)
93195fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
93295fa0405SHaiyang Zhang 	return;
93395fa0405SHaiyang Zhang }
93495fa0405SHaiyang Zhang 
93595fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
93695fa0405SHaiyang Zhang {
93795fa0405SHaiyang Zhang 	int ret;
93895fa0405SHaiyang Zhang 
93995fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
94095fa0405SHaiyang Zhang 		return 0;
94195fa0405SHaiyang Zhang 
94295fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
94395fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
94495fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
94595fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
94695fa0405SHaiyang Zhang 	if (ret == 0)
94795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
94895fa0405SHaiyang Zhang 
94995fa0405SHaiyang Zhang 	return ret;
95095fa0405SHaiyang Zhang }
95195fa0405SHaiyang Zhang 
95295fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
95395fa0405SHaiyang Zhang {
95495fa0405SHaiyang Zhang 	int ret;
95595fa0405SHaiyang Zhang 
95695fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
95795fa0405SHaiyang Zhang 		return 0;
95895fa0405SHaiyang Zhang 
95995fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
96095fa0405SHaiyang Zhang 	if (ret == 0)
96195fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
96295fa0405SHaiyang Zhang 
96395fa0405SHaiyang Zhang 	return ret;
96495fa0405SHaiyang Zhang }
96595fa0405SHaiyang Zhang 
9665b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
9675b54dac8SHaiyang Zhang {
9685b54dac8SHaiyang Zhang 	struct netvsc_device *nvscdev;
9695b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
9705b54dac8SHaiyang Zhang 	int ret;
9715b54dac8SHaiyang Zhang 
9725b54dac8SHaiyang Zhang 	nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj);
9735b54dac8SHaiyang Zhang 
9745b54dac8SHaiyang Zhang 	if (chn_index >= nvscdev->num_chn)
9755b54dac8SHaiyang Zhang 		return;
9765b54dac8SHaiyang Zhang 
9775b54dac8SHaiyang Zhang 	set_per_channel_state(new_sc, nvscdev->sub_cb_buf + (chn_index - 1) *
9785b54dac8SHaiyang Zhang 			      NETVSC_PACKET_SIZE);
9795b54dac8SHaiyang Zhang 
9805b54dac8SHaiyang Zhang 	ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
9815b54dac8SHaiyang Zhang 			 nvscdev->ring_size * PAGE_SIZE, NULL, 0,
9825b54dac8SHaiyang Zhang 			 netvsc_channel_cb, new_sc);
9835b54dac8SHaiyang Zhang 
9845b54dac8SHaiyang Zhang 	if (ret == 0)
9855b54dac8SHaiyang Zhang 		nvscdev->chn_table[chn_index] = new_sc;
9865b54dac8SHaiyang Zhang }
9875b54dac8SHaiyang Zhang 
98895fa0405SHaiyang Zhang int rndis_filter_device_add(struct hv_device *dev,
98995fa0405SHaiyang Zhang 				  void *additional_info)
99095fa0405SHaiyang Zhang {
99195fa0405SHaiyang Zhang 	int ret;
99295fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
99395fa0405SHaiyang Zhang 	struct rndis_device *rndis_device;
99495fa0405SHaiyang Zhang 	struct netvsc_device_info *device_info = additional_info;
9954a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
9965b54dac8SHaiyang Zhang 	struct nvsp_message *init_packet;
9975b54dac8SHaiyang Zhang 	int t;
9985b54dac8SHaiyang Zhang 	struct ndis_recv_scale_cap rsscap;
9995b54dac8SHaiyang Zhang 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
100095fa0405SHaiyang Zhang 
100195fa0405SHaiyang Zhang 	rndis_device = get_rndis_device();
100295fa0405SHaiyang Zhang 	if (!rndis_device)
100395fa0405SHaiyang Zhang 		return -ENODEV;
100495fa0405SHaiyang Zhang 
100595fa0405SHaiyang Zhang 	/*
100695fa0405SHaiyang Zhang 	 * Let the inner driver handle this first to create the netvsc channel
100795fa0405SHaiyang Zhang 	 * NOTE! Once the channel is created, we may get a receive callback
100895fa0405SHaiyang Zhang 	 * (RndisFilterOnReceive()) before this call is completed
100995fa0405SHaiyang Zhang 	 */
101095fa0405SHaiyang Zhang 	ret = netvsc_device_add(dev, additional_info);
101195fa0405SHaiyang Zhang 	if (ret != 0) {
101295fa0405SHaiyang Zhang 		kfree(rndis_device);
101395fa0405SHaiyang Zhang 		return ret;
101495fa0405SHaiyang Zhang 	}
101595fa0405SHaiyang Zhang 
101695fa0405SHaiyang Zhang 
101795fa0405SHaiyang Zhang 	/* Initialize the rndis device */
101895fa0405SHaiyang Zhang 	net_device = hv_get_drvdata(dev);
10195b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
102095fa0405SHaiyang Zhang 
102195fa0405SHaiyang Zhang 	net_device->extension = rndis_device;
102295fa0405SHaiyang Zhang 	rndis_device->net_dev = net_device;
102395fa0405SHaiyang Zhang 
102495fa0405SHaiyang Zhang 	/* Send the rndis initialization message */
102595fa0405SHaiyang Zhang 	ret = rndis_filter_init_device(rndis_device);
102695fa0405SHaiyang Zhang 	if (ret != 0) {
10275243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
10285243e7bdSHaiyang Zhang 		return ret;
102995fa0405SHaiyang Zhang 	}
103095fa0405SHaiyang Zhang 
103195fa0405SHaiyang Zhang 	/* Get the mac address */
103295fa0405SHaiyang Zhang 	ret = rndis_filter_query_device_mac(rndis_device);
103395fa0405SHaiyang Zhang 	if (ret != 0) {
10345243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
10355243e7bdSHaiyang Zhang 		return ret;
103695fa0405SHaiyang Zhang 	}
103795fa0405SHaiyang Zhang 
103895fa0405SHaiyang Zhang 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
103995fa0405SHaiyang Zhang 
10404a0e70aeSKY Srinivasan 	/* Turn on the offloads; the host supports all of the relevant
10414a0e70aeSKY Srinivasan 	 * offloads.
10424a0e70aeSKY Srinivasan 	 */
10434a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
10444a0e70aeSKY Srinivasan 	/* A value of zero means "no change"; now turn on what we
10454a0e70aeSKY Srinivasan 	 * want.
10464a0e70aeSKY Srinivasan 	 */
10474a0e70aeSKY Srinivasan 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10484a0e70aeSKY Srinivasan 	offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10494a0e70aeSKY Srinivasan 	offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10504a0e70aeSKY Srinivasan 	offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10514a0e70aeSKY Srinivasan 	offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
10524a0e70aeSKY Srinivasan 	offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
10534a0e70aeSKY Srinivasan 
10544a0e70aeSKY Srinivasan 
10554a0e70aeSKY Srinivasan 	ret = rndis_filter_set_offload_params(dev, &offloads);
10564a0e70aeSKY Srinivasan 	if (ret)
10574a0e70aeSKY Srinivasan 		goto err_dev_remv;
10584a0e70aeSKY Srinivasan 
105995fa0405SHaiyang Zhang 	rndis_filter_query_device_link_status(rndis_device);
106095fa0405SHaiyang Zhang 
106195fa0405SHaiyang Zhang 	device_info->link_state = rndis_device->link_state;
106295fa0405SHaiyang Zhang 
106395fa0405SHaiyang Zhang 	dev_info(&dev->device, "Device MAC %pM link state %s\n",
106495fa0405SHaiyang Zhang 		 rndis_device->hw_mac_adr,
106595fa0405SHaiyang Zhang 		 device_info->link_state ? "down" : "up");
106695fa0405SHaiyang Zhang 
10675b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
10685b54dac8SHaiyang Zhang 		return 0;
10695b54dac8SHaiyang Zhang 
10705b54dac8SHaiyang Zhang 	/* vRSS setup */
10715b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
10725b54dac8SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
10735b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
10745b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
10755b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
10765b54dac8SHaiyang Zhang 		goto out;
10775b54dac8SHaiyang Zhang 
10785b54dac8SHaiyang Zhang 	net_device->num_chn = (num_online_cpus() < rsscap.num_recv_que) ?
10795b54dac8SHaiyang Zhang 			       num_online_cpus() : rsscap.num_recv_que;
10805b54dac8SHaiyang Zhang 	if (net_device->num_chn == 1)
10815b54dac8SHaiyang Zhang 		goto out;
10825b54dac8SHaiyang Zhang 
10835b54dac8SHaiyang Zhang 	net_device->sub_cb_buf = vzalloc((net_device->num_chn - 1) *
10845b54dac8SHaiyang Zhang 					 NETVSC_PACKET_SIZE);
10855b54dac8SHaiyang Zhang 	if (!net_device->sub_cb_buf) {
10865b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
10875b54dac8SHaiyang Zhang 		dev_info(&dev->device, "No memory for subchannels.\n");
10885b54dac8SHaiyang Zhang 		goto out;
10895b54dac8SHaiyang Zhang 	}
10905b54dac8SHaiyang Zhang 
10915b54dac8SHaiyang Zhang 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
10925b54dac8SHaiyang Zhang 
10935b54dac8SHaiyang Zhang 	init_packet = &net_device->channel_init_pkt;
10945b54dac8SHaiyang Zhang 	memset(init_packet, 0, sizeof(struct nvsp_message));
10955b54dac8SHaiyang Zhang 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
10965b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
10975b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
10985b54dac8SHaiyang Zhang 						net_device->num_chn - 1;
10995b54dac8SHaiyang Zhang 	ret = vmbus_sendpacket(dev->channel, init_packet,
11005b54dac8SHaiyang Zhang 			       sizeof(struct nvsp_message),
11015b54dac8SHaiyang Zhang 			       (unsigned long)init_packet,
11025b54dac8SHaiyang Zhang 			       VM_PKT_DATA_INBAND,
11035b54dac8SHaiyang Zhang 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
11045b54dac8SHaiyang Zhang 	if (ret)
11055b54dac8SHaiyang Zhang 		goto out;
11065b54dac8SHaiyang Zhang 	t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
11075b54dac8SHaiyang Zhang 	if (t == 0) {
11085b54dac8SHaiyang Zhang 		ret = -ETIMEDOUT;
11095b54dac8SHaiyang Zhang 		goto out;
11105b54dac8SHaiyang Zhang 	}
11115b54dac8SHaiyang Zhang 	if (init_packet->msg.v5_msg.subchn_comp.status !=
11125b54dac8SHaiyang Zhang 	    NVSP_STAT_SUCCESS) {
11135b54dac8SHaiyang Zhang 		ret = -ENODEV;
11145b54dac8SHaiyang Zhang 		goto out;
11155b54dac8SHaiyang Zhang 	}
11165b54dac8SHaiyang Zhang 	net_device->num_chn = 1 +
11175b54dac8SHaiyang Zhang 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
11185b54dac8SHaiyang Zhang 
11195b54dac8SHaiyang Zhang 	vmbus_are_subchannels_present(dev->channel);
11205b54dac8SHaiyang Zhang 
11215b54dac8SHaiyang Zhang 	ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn);
11225b54dac8SHaiyang Zhang 
11235b54dac8SHaiyang Zhang out:
11245b54dac8SHaiyang Zhang 	if (ret)
11255b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
11265b54dac8SHaiyang Zhang 	return 0; /* return 0 because primary channel can be used alone */
11274a0e70aeSKY Srinivasan 
11284a0e70aeSKY Srinivasan err_dev_remv:
11294a0e70aeSKY Srinivasan 	rndis_filter_device_remove(dev);
11304a0e70aeSKY Srinivasan 	return ret;
113195fa0405SHaiyang Zhang }
113295fa0405SHaiyang Zhang 
113395fa0405SHaiyang Zhang void rndis_filter_device_remove(struct hv_device *dev)
113495fa0405SHaiyang Zhang {
113595fa0405SHaiyang Zhang 	struct netvsc_device *net_dev = hv_get_drvdata(dev);
113695fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
113795fa0405SHaiyang Zhang 
113895fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
113995fa0405SHaiyang Zhang 	rndis_filter_halt_device(rndis_dev);
114095fa0405SHaiyang Zhang 
114195fa0405SHaiyang Zhang 	kfree(rndis_dev);
114295fa0405SHaiyang Zhang 	net_dev->extension = NULL;
114395fa0405SHaiyang Zhang 
114495fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
114595fa0405SHaiyang Zhang }
114695fa0405SHaiyang Zhang 
114795fa0405SHaiyang Zhang 
114895fa0405SHaiyang Zhang int rndis_filter_open(struct hv_device *dev)
114995fa0405SHaiyang Zhang {
115095fa0405SHaiyang Zhang 	struct netvsc_device *net_device = hv_get_drvdata(dev);
115195fa0405SHaiyang Zhang 
115295fa0405SHaiyang Zhang 	if (!net_device)
115395fa0405SHaiyang Zhang 		return -EINVAL;
115495fa0405SHaiyang Zhang 
115595fa0405SHaiyang Zhang 	return rndis_filter_open_device(net_device->extension);
115695fa0405SHaiyang Zhang }
115795fa0405SHaiyang Zhang 
115895fa0405SHaiyang Zhang int rndis_filter_close(struct hv_device *dev)
115995fa0405SHaiyang Zhang {
11605fccab3bSHaiyang Zhang 	struct netvsc_device *nvdev = hv_get_drvdata(dev);
116195fa0405SHaiyang Zhang 
11625fccab3bSHaiyang Zhang 	if (!nvdev)
116395fa0405SHaiyang Zhang 		return -EINVAL;
116495fa0405SHaiyang Zhang 
11655fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
116695fa0405SHaiyang Zhang }
1167