195fa0405SHaiyang Zhang /*
295fa0405SHaiyang Zhang  * Copyright (c) 2009, Microsoft Corporation.
395fa0405SHaiyang Zhang  *
495fa0405SHaiyang Zhang  * This program is free software; you can redistribute it and/or modify it
595fa0405SHaiyang Zhang  * under the terms and conditions of the GNU General Public License,
695fa0405SHaiyang Zhang  * version 2, as published by the Free Software Foundation.
795fa0405SHaiyang Zhang  *
895fa0405SHaiyang Zhang  * This program is distributed in the hope it will be useful, but WITHOUT
995fa0405SHaiyang Zhang  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1095fa0405SHaiyang Zhang  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1195fa0405SHaiyang Zhang  * more details.
1295fa0405SHaiyang Zhang  *
1395fa0405SHaiyang Zhang  * You should have received a copy of the GNU General Public License along with
14adf8d3ffSJeff Kirsher  * this program; if not, see <http://www.gnu.org/licenses/>.
1595fa0405SHaiyang Zhang  *
1695fa0405SHaiyang Zhang  * Authors:
1795fa0405SHaiyang Zhang  *   Haiyang Zhang <haiyangz@microsoft.com>
1895fa0405SHaiyang Zhang  *   Hank Janssen  <hjanssen@microsoft.com>
1995fa0405SHaiyang Zhang  */
2095fa0405SHaiyang Zhang #include <linux/kernel.h>
2195fa0405SHaiyang Zhang #include <linux/sched.h>
2295fa0405SHaiyang Zhang #include <linux/wait.h>
2395fa0405SHaiyang Zhang #include <linux/highmem.h>
2495fa0405SHaiyang Zhang #include <linux/slab.h>
2595fa0405SHaiyang Zhang #include <linux/io.h>
2695fa0405SHaiyang Zhang #include <linux/if_ether.h>
2795fa0405SHaiyang Zhang #include <linux/netdevice.h>
281f5f3a75SHaiyang Zhang #include <linux/if_vlan.h>
291ce09e89SHaiyang Zhang #include <linux/nls.h>
30d6472302SStephen Rothwell #include <linux/vmalloc.h>
3127f5aa92Sstephen hemminger #include <linux/rtnetlink.h>
320fe554a4SStephen Hemminger #include <linux/ucs2_string.h>
3395fa0405SHaiyang Zhang 
3495fa0405SHaiyang Zhang #include "hyperv_net.h"
35ec966381SStephen Hemminger #include "netvsc_trace.h"
3695fa0405SHaiyang Zhang 
374f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w);
3895fa0405SHaiyang Zhang 
395b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
4095fa0405SHaiyang Zhang struct rndis_request {
4195fa0405SHaiyang Zhang 	struct list_head list_ent;
4295fa0405SHaiyang Zhang 	struct completion  wait_event;
4395fa0405SHaiyang Zhang 
4495fa0405SHaiyang Zhang 	struct rndis_message response_msg;
45a3a6cab5SHaiyang Zhang 	/*
46a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
47a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
48a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
49a3a6cab5SHaiyang Zhang 	 * future.
50a3a6cab5SHaiyang Zhang 	 */
51a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
5295fa0405SHaiyang Zhang 
5395fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
5495fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
550f48917bSHaiyang Zhang 
5695fa0405SHaiyang Zhang 	struct rndis_message request_msg;
570f48917bSHaiyang Zhang 	/*
58a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
59a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
600f48917bSHaiyang Zhang 	 */
61a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
6295fa0405SHaiyang Zhang };
6395fa0405SHaiyang Zhang 
64962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
65962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
66962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
67962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
68962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
69962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
70962f3feeSstephen hemminger };
71962f3feeSstephen hemminger 
7295fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
7395fa0405SHaiyang Zhang {
7495fa0405SHaiyang Zhang 	struct rndis_device *device;
7595fa0405SHaiyang Zhang 
7695fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
7795fa0405SHaiyang Zhang 	if (!device)
7895fa0405SHaiyang Zhang 		return NULL;
7995fa0405SHaiyang Zhang 
8095fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
8195fa0405SHaiyang Zhang 
8295fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
834f19c0d8Sstephen hemminger 	INIT_WORK(&device->mcast_work, rndis_set_multicast);
8495fa0405SHaiyang Zhang 
8595fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
8695fa0405SHaiyang Zhang 
8795fa0405SHaiyang Zhang 	return device;
8895fa0405SHaiyang Zhang }
8995fa0405SHaiyang Zhang 
9095fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
9195fa0405SHaiyang Zhang 					     u32 msg_type,
9295fa0405SHaiyang Zhang 					     u32 msg_len)
9395fa0405SHaiyang Zhang {
9495fa0405SHaiyang Zhang 	struct rndis_request *request;
9595fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
9695fa0405SHaiyang Zhang 	struct rndis_set_request *set;
9795fa0405SHaiyang Zhang 	unsigned long flags;
9895fa0405SHaiyang Zhang 
9995fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
10095fa0405SHaiyang Zhang 	if (!request)
10195fa0405SHaiyang Zhang 		return NULL;
10295fa0405SHaiyang Zhang 
10395fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
10495fa0405SHaiyang Zhang 
10595fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
10695fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
10795fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
10895fa0405SHaiyang Zhang 
1095b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1105b54dac8SHaiyang Zhang 
11195fa0405SHaiyang Zhang 	/*
11295fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
11395fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
11495fa0405SHaiyang Zhang 	 * template
11595fa0405SHaiyang Zhang 	 */
11695fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
11795fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
11895fa0405SHaiyang Zhang 
11995fa0405SHaiyang Zhang 	/* Add to the request list */
12095fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12195fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
12295fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12395fa0405SHaiyang Zhang 
12495fa0405SHaiyang Zhang 	return request;
12595fa0405SHaiyang Zhang }
12695fa0405SHaiyang Zhang 
12795fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
12895fa0405SHaiyang Zhang 			    struct rndis_request *req)
12995fa0405SHaiyang Zhang {
13095fa0405SHaiyang Zhang 	unsigned long flags;
13195fa0405SHaiyang Zhang 
13295fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
13395fa0405SHaiyang Zhang 	list_del(&req->list_ent);
13495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
13595fa0405SHaiyang Zhang 
13695fa0405SHaiyang Zhang 	kfree(req);
13795fa0405SHaiyang Zhang }
13895fa0405SHaiyang Zhang 
13979cf1baeSStephen Hemminger static void dump_rndis_message(struct net_device *netdev,
140dc54a08cSstephen hemminger 			       const struct rndis_message *rndis_msg)
14195fa0405SHaiyang Zhang {
14295fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
14351491167SLinus Walleij 	case RNDIS_MSG_PACKET:
14451491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14595fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14695fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14795fa0405SHaiyang Zhang 			   "pkt len %u\n",
14895fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
15095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
15195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
15295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
15395fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
15495fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
15595fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15695fa0405SHaiyang Zhang 		break;
15795fa0405SHaiyang Zhang 
15851491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15951491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
16095fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
16195fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
16295fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
16395fa0405SHaiyang Zhang 			rndis_msg->msg_len,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16895fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
17095fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17195fa0405SHaiyang Zhang 			   max_pkt_per_msg,
17295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17395fa0405SHaiyang Zhang 			   pkt_alignment_factor);
17495fa0405SHaiyang Zhang 		break;
17595fa0405SHaiyang Zhang 
17651491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17751491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17895fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17995fa0405SHaiyang Zhang 			"buf offset %u)\n",
18095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
18195fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
18295fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
18395fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18495fa0405SHaiyang Zhang 			   info_buflen,
18595fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18695fa0405SHaiyang Zhang 			   info_buf_offset);
18795fa0405SHaiyang Zhang 		break;
18895fa0405SHaiyang Zhang 
18951491167SLinus Walleij 	case RNDIS_MSG_SET_C:
19095fa0405SHaiyang Zhang 		netdev_dbg(netdev,
19151491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19295fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19395fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
19495fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
19595fa0405SHaiyang Zhang 		break;
19695fa0405SHaiyang Zhang 
19751491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19851491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19995fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
20095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
20195fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
20295fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
20395fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
20495fa0405SHaiyang Zhang 		break;
20595fa0405SHaiyang Zhang 
20695fa0405SHaiyang Zhang 	default:
20795fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20895fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20995fa0405SHaiyang Zhang 			rndis_msg->msg_len);
21095fa0405SHaiyang Zhang 		break;
21195fa0405SHaiyang Zhang 	}
21295fa0405SHaiyang Zhang }
21395fa0405SHaiyang Zhang 
21495fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
21595fa0405SHaiyang Zhang 				  struct rndis_request *req)
21695fa0405SHaiyang Zhang {
21795fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
218b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
219a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
22002b6de01Sstephen hemminger 	int ret;
22195fa0405SHaiyang Zhang 
22295fa0405SHaiyang Zhang 	/* Setup the packet to send it */
22395fa0405SHaiyang Zhang 	packet = &req->pkt;
22495fa0405SHaiyang Zhang 
22595fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
22695fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22795fa0405SHaiyang Zhang 
228a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
22995fa0405SHaiyang Zhang 					PAGE_SHIFT;
230a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
231a9f2e2d6SKY Srinivasan 	pb[0].offset =
23295fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
23395fa0405SHaiyang Zhang 
23499e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
235a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
23699e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
237a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
238a9f2e2d6SKY Srinivasan 			pb[0].offset;
239a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
240a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
241a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
242a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
243a9f2e2d6SKY Srinivasan 			pb[0].len;
24499e3fcfaSHaiyang Zhang 	}
24599e3fcfaSHaiyang Zhang 
246ec966381SStephen Hemminger 	trace_rndis_send(dev->ndev, 0, &req->request_msg);
247ec966381SStephen Hemminger 
248867047c4Sstephen hemminger 	rcu_read_lock_bh();
249cfd8afd9SStephen Hemminger 	ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL);
250867047c4Sstephen hemminger 	rcu_read_unlock_bh();
251867047c4Sstephen hemminger 
25295fa0405SHaiyang Zhang 	return ret;
25395fa0405SHaiyang Zhang }
25495fa0405SHaiyang Zhang 
2551b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2561b07da51SHaiyang Zhang 				 struct rndis_request *request)
2571b07da51SHaiyang Zhang {
2581b07da51SHaiyang Zhang 	u32 link_status;
2591b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2601b07da51SHaiyang Zhang 
2611b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2621b07da51SHaiyang Zhang 
2631b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2641b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2651b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2661b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2671b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2681b07da51SHaiyang Zhang 	}
2691b07da51SHaiyang Zhang }
2701b07da51SHaiyang Zhang 
27102400fceSStephen Hemminger static void rndis_filter_receive_response(struct net_device *ndev,
27202400fceSStephen Hemminger 					  struct netvsc_device *nvdev,
27302400fceSStephen Hemminger 					  const struct rndis_message *resp)
27495fa0405SHaiyang Zhang {
27502400fceSStephen Hemminger 	struct rndis_device *dev = nvdev->extension;
27695fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
27795fa0405SHaiyang Zhang 	bool found = false;
27895fa0405SHaiyang Zhang 	unsigned long flags;
27902400fceSStephen Hemminger 
28002400fceSStephen Hemminger 	/* This should never happen, it means control message
28102400fceSStephen Hemminger 	 * response received after device removed.
28202400fceSStephen Hemminger 	 */
28302400fceSStephen Hemminger 	if (dev->state == RNDIS_DEV_UNINITIALIZED) {
28402400fceSStephen Hemminger 		netdev_err(ndev,
28502400fceSStephen Hemminger 			   "got rndis message uninitialized\n");
28602400fceSStephen Hemminger 		return;
28702400fceSStephen Hemminger 	}
28895fa0405SHaiyang Zhang 
28995fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
29095fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
29195fa0405SHaiyang Zhang 		/*
29295fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
29395fa0405SHaiyang Zhang 		 * field
29495fa0405SHaiyang Zhang 		 */
29595fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
29695fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
29795fa0405SHaiyang Zhang 			found = true;
29895fa0405SHaiyang Zhang 			break;
29995fa0405SHaiyang Zhang 		}
30095fa0405SHaiyang Zhang 	}
30195fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
30295fa0405SHaiyang Zhang 
30395fa0405SHaiyang Zhang 	if (found) {
304a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
305a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
30695fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
30795fa0405SHaiyang Zhang 			       resp->msg_len);
3081b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
3091b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
3101b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
3111b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
31295fa0405SHaiyang Zhang 		} else {
31395fa0405SHaiyang Zhang 			netdev_err(ndev,
31495fa0405SHaiyang Zhang 				"rndis response buffer overflow "
31595fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
31695fa0405SHaiyang Zhang 				resp->msg_len,
31786eedaccSKY Srinivasan 				sizeof(struct rndis_message));
31895fa0405SHaiyang Zhang 
31995fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
32051491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
32195fa0405SHaiyang Zhang 				/* does not have a request id field */
32295fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
323007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
32495fa0405SHaiyang Zhang 			} else {
32595fa0405SHaiyang Zhang 				request->response_msg.msg.
32695fa0405SHaiyang Zhang 				init_complete.status =
327007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
32895fa0405SHaiyang Zhang 			}
32995fa0405SHaiyang Zhang 		}
33095fa0405SHaiyang Zhang 
33195fa0405SHaiyang Zhang 		complete(&request->wait_event);
33295fa0405SHaiyang Zhang 	} else {
33395fa0405SHaiyang Zhang 		netdev_err(ndev,
33495fa0405SHaiyang Zhang 			"no rndis request found for this response "
33595fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
33695fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
33795fa0405SHaiyang Zhang 			resp->ndis_msg_type);
33895fa0405SHaiyang Zhang 	}
33995fa0405SHaiyang Zhang }
34095fa0405SHaiyang Zhang 
3411f5f3a75SHaiyang Zhang /*
3421f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3431f5f3a75SHaiyang Zhang  * return NULL if not found.
3441f5f3a75SHaiyang Zhang  */
345c8e4eff4SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt,
346c8e4eff4SHaiyang Zhang 				  u32 type, u8 internal)
3471f5f3a75SHaiyang Zhang {
3481f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3491f5f3a75SHaiyang Zhang 	int len;
3501f5f3a75SHaiyang Zhang 
3511f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3521f5f3a75SHaiyang Zhang 		return NULL;
3531f5f3a75SHaiyang Zhang 
3541f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3551f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3561f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3571f5f3a75SHaiyang Zhang 
3581f5f3a75SHaiyang Zhang 	while (len > 0) {
359c8e4eff4SHaiyang Zhang 		if (ppi->type == type && ppi->internal == internal)
3601f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3611f5f3a75SHaiyang Zhang 		len -= ppi->size;
3621f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3631f5f3a75SHaiyang Zhang 	}
3641f5f3a75SHaiyang Zhang 
3651f5f3a75SHaiyang Zhang 	return NULL;
3661f5f3a75SHaiyang Zhang }
3671f5f3a75SHaiyang Zhang 
368c8e4eff4SHaiyang Zhang static inline
369c8e4eff4SHaiyang Zhang void rsc_add_data(struct netvsc_channel *nvchan,
370c8e4eff4SHaiyang Zhang 		  const struct ndis_pkt_8021q_info *vlan,
371c8e4eff4SHaiyang Zhang 		  const struct ndis_tcp_ip_checksum_info *csum_info,
372c8e4eff4SHaiyang Zhang 		  void *data, u32 len)
373c8e4eff4SHaiyang Zhang {
374c8e4eff4SHaiyang Zhang 	u32 cnt = nvchan->rsc.cnt;
375c8e4eff4SHaiyang Zhang 
376c8e4eff4SHaiyang Zhang 	if (cnt) {
377c8e4eff4SHaiyang Zhang 		nvchan->rsc.pktlen += len;
378c8e4eff4SHaiyang Zhang 	} else {
379c8e4eff4SHaiyang Zhang 		nvchan->rsc.vlan = vlan;
380c8e4eff4SHaiyang Zhang 		nvchan->rsc.csum_info = csum_info;
381c8e4eff4SHaiyang Zhang 		nvchan->rsc.pktlen = len;
382c8e4eff4SHaiyang Zhang 	}
383c8e4eff4SHaiyang Zhang 
384c8e4eff4SHaiyang Zhang 	nvchan->rsc.data[cnt] = data;
385c8e4eff4SHaiyang Zhang 	nvchan->rsc.len[cnt] = len;
386c8e4eff4SHaiyang Zhang 	nvchan->rsc.cnt++;
387c8e4eff4SHaiyang Zhang }
388c8e4eff4SHaiyang Zhang 
389dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
390345ac089SStephen Hemminger 				     struct netvsc_device *nvdev,
391c8e4eff4SHaiyang Zhang 				     struct netvsc_channel *nvchan,
3923be9b5fdSHaiyang Zhang 				     struct rndis_message *msg,
3933be9b5fdSHaiyang Zhang 				     u32 data_buflen)
39495fa0405SHaiyang Zhang {
395dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
396dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
397dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
398c8e4eff4SHaiyang Zhang 	const struct rndis_pktinfo_id *pktinfo_id;
39995fa0405SHaiyang Zhang 	u32 data_offset;
4003be9b5fdSHaiyang Zhang 	void *data;
401c8e4eff4SHaiyang Zhang 	bool rsc_more = false;
402c8e4eff4SHaiyang Zhang 	int ret;
40395fa0405SHaiyang Zhang 
40495fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
40595fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
40695fa0405SHaiyang Zhang 
407dc54a08cSstephen hemminger 	data_buflen -= data_offset;
4084b8a8bc9SWei Yongjun 
4094b8a8bc9SWei Yongjun 	/*
4104b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
4114b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
4124b8a8bc9SWei Yongjun 	 */
413dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
41402400fceSStephen Hemminger 		netdev_err(ndev, "rndis message buffer "
4154b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
4164b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
417dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
41810082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
4194b8a8bc9SWei Yongjun 	}
4204b8a8bc9SWei Yongjun 
421c8e4eff4SHaiyang Zhang 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO, 0);
422dc54a08cSstephen hemminger 
423c8e4eff4SHaiyang Zhang 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO, 0);
424c8e4eff4SHaiyang Zhang 
425c8e4eff4SHaiyang Zhang 	pktinfo_id = rndis_get_ppi(rndis_pkt, RNDIS_PKTINFO_ID, 1);
4263be9b5fdSHaiyang Zhang 
4273be9b5fdSHaiyang Zhang 	data = (void *)msg + data_offset;
4283be9b5fdSHaiyang Zhang 
429c8e4eff4SHaiyang Zhang 	/* Identify RSC frags, drop erroneous packets */
430c8e4eff4SHaiyang Zhang 	if (pktinfo_id && (pktinfo_id->flag & RNDIS_PKTINFO_SUBALLOC)) {
431c8e4eff4SHaiyang Zhang 		if (pktinfo_id->flag & RNDIS_PKTINFO_1ST_FRAG)
432c8e4eff4SHaiyang Zhang 			nvchan->rsc.cnt = 0;
433c8e4eff4SHaiyang Zhang 		else if (nvchan->rsc.cnt == 0)
434c8e4eff4SHaiyang Zhang 			goto drop;
435c8e4eff4SHaiyang Zhang 
436c8e4eff4SHaiyang Zhang 		rsc_more = true;
437c8e4eff4SHaiyang Zhang 
438c8e4eff4SHaiyang Zhang 		if (pktinfo_id->flag & RNDIS_PKTINFO_LAST_FRAG)
439c8e4eff4SHaiyang Zhang 			rsc_more = false;
440c8e4eff4SHaiyang Zhang 
441c8e4eff4SHaiyang Zhang 		if (rsc_more && nvchan->rsc.is_last)
442c8e4eff4SHaiyang Zhang 			goto drop;
443c8e4eff4SHaiyang Zhang 	} else {
444c8e4eff4SHaiyang Zhang 		nvchan->rsc.cnt = 0;
445c8e4eff4SHaiyang Zhang 	}
446c8e4eff4SHaiyang Zhang 
447c8e4eff4SHaiyang Zhang 	if (unlikely(nvchan->rsc.cnt >= NVSP_RSC_MAX))
448c8e4eff4SHaiyang Zhang 		goto drop;
449c8e4eff4SHaiyang Zhang 
450c8e4eff4SHaiyang Zhang 	/* Put data into per channel structure.
451c8e4eff4SHaiyang Zhang 	 * Also, remove the rndis trailer padding from rndis packet message
4524b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
4534b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
4544b8a8bc9SWei Yongjun 	 */
455c8e4eff4SHaiyang Zhang 	rsc_add_data(nvchan, vlan, csum_info, data, rndis_pkt->data_len);
456c8e4eff4SHaiyang Zhang 
457c8e4eff4SHaiyang Zhang 	if (rsc_more)
458c8e4eff4SHaiyang Zhang 		return NVSP_STAT_SUCCESS;
459c8e4eff4SHaiyang Zhang 
460c8e4eff4SHaiyang Zhang 	ret = netvsc_recv_callback(ndev, nvdev, nvchan);
461c8e4eff4SHaiyang Zhang 	nvchan->rsc.cnt = 0;
462c8e4eff4SHaiyang Zhang 
463c8e4eff4SHaiyang Zhang 	return ret;
464c8e4eff4SHaiyang Zhang 
465c8e4eff4SHaiyang Zhang drop:
466c8e4eff4SHaiyang Zhang 	/* Drop incomplete packet */
467c8e4eff4SHaiyang Zhang 	nvchan->rsc.cnt = 0;
468c8e4eff4SHaiyang Zhang 	return NVSP_STAT_FAIL;
46995fa0405SHaiyang Zhang }
47095fa0405SHaiyang Zhang 
471dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
472dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
473c8e4eff4SHaiyang Zhang 			 struct netvsc_channel *nvchan,
474dc54a08cSstephen hemminger 			 void *data, u32 buflen)
47595fa0405SHaiyang Zhang {
4763d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
477dc54a08cSstephen hemminger 	struct rndis_message *rndis_msg = data;
47895fa0405SHaiyang Zhang 
479dc54a08cSstephen hemminger 	if (netif_msg_rx_status(net_device_ctx))
48079cf1baeSStephen Hemminger 		dump_rndis_message(ndev, rndis_msg);
48195fa0405SHaiyang Zhang 
482ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
48351491167SLinus Walleij 	case RNDIS_MSG_PACKET:
484c8e4eff4SHaiyang Zhang 		return rndis_filter_receive_data(ndev, net_dev, nvchan,
4853be9b5fdSHaiyang Zhang 						 rndis_msg, buflen);
48651491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
48751491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
48851491167SLinus Walleij 	case RNDIS_MSG_SET_C:
48995fa0405SHaiyang Zhang 		/* completion msgs */
49002400fceSStephen Hemminger 		rndis_filter_receive_response(ndev, net_dev, rndis_msg);
49195fa0405SHaiyang Zhang 		break;
49295fa0405SHaiyang Zhang 
49351491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
49495fa0405SHaiyang Zhang 		/* notification msgs */
49579cf1baeSStephen Hemminger 		netvsc_linkstatus_callback(ndev, rndis_msg);
49695fa0405SHaiyang Zhang 		break;
49795fa0405SHaiyang Zhang 	default:
49895fa0405SHaiyang Zhang 		netdev_err(ndev,
49995fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
500ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
501ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
5025c71dadbSHaiyang Zhang 		return NVSP_STAT_FAIL;
50395fa0405SHaiyang Zhang 	}
50495fa0405SHaiyang Zhang 
5055c71dadbSHaiyang Zhang 	return NVSP_STAT_SUCCESS;
50695fa0405SHaiyang Zhang }
50795fa0405SHaiyang Zhang 
508867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev,
509867047c4Sstephen hemminger 				     struct netvsc_device *nvdev,
510867047c4Sstephen hemminger 				     u32 oid, void *result, u32 *result_size)
51195fa0405SHaiyang Zhang {
51295fa0405SHaiyang Zhang 	struct rndis_request *request;
51395fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
51495fa0405SHaiyang Zhang 	struct rndis_query_request *query;
51595fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
51695fa0405SHaiyang Zhang 	int ret = 0;
51795fa0405SHaiyang Zhang 
51895fa0405SHaiyang Zhang 	if (!result)
51995fa0405SHaiyang Zhang 		return -EINVAL;
52095fa0405SHaiyang Zhang 
52195fa0405SHaiyang Zhang 	*result_size = 0;
52251491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
52395fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
52495fa0405SHaiyang Zhang 	if (!request) {
52595fa0405SHaiyang Zhang 		ret = -ENOMEM;
52695fa0405SHaiyang Zhang 		goto cleanup;
52795fa0405SHaiyang Zhang 	}
52895fa0405SHaiyang Zhang 
52995fa0405SHaiyang Zhang 	/* Setup the rndis query */
53095fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
53195fa0405SHaiyang Zhang 	query->oid = oid;
53295fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
53395fa0405SHaiyang Zhang 	query->info_buflen = 0;
53495fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
53595fa0405SHaiyang Zhang 
53623312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
53723312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
53823312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
53923312a3bSstephen hemminger 		u8 ndis_rev;
54023312a3bSstephen hemminger 		size_t size;
54123312a3bSstephen hemminger 
54223312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
54323312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
54423312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
54523312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
54623312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
54723312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
54823312a3bSstephen hemminger 		} else {
54923312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
55023312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
55123312a3bSstephen hemminger 		}
55223312a3bSstephen hemminger 
55323312a3bSstephen hemminger 		request->request_msg.msg_len += size;
55423312a3bSstephen hemminger 		query->info_buflen = size;
55523312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
55623312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
55723312a3bSstephen hemminger 
55823312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
55923312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
56023312a3bSstephen hemminger 		hwcaps->header.size = size;
56123312a3bSstephen hemminger 
56223312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
5635b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
5645b54dac8SHaiyang Zhang 
5655b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5665b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5675b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5685b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5695b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5705b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5715b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5725b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5735b54dac8SHaiyang Zhang 	}
5745b54dac8SHaiyang Zhang 
57595fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
57695fa0405SHaiyang Zhang 	if (ret != 0)
57795fa0405SHaiyang Zhang 		goto cleanup;
57895fa0405SHaiyang Zhang 
5795362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
58095fa0405SHaiyang Zhang 
58195fa0405SHaiyang Zhang 	/* Copy the response back */
58295fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
58395fa0405SHaiyang Zhang 
58495fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
58595fa0405SHaiyang Zhang 		ret = -1;
58695fa0405SHaiyang Zhang 		goto cleanup;
58795fa0405SHaiyang Zhang 	}
58895fa0405SHaiyang Zhang 
58995fa0405SHaiyang Zhang 	memcpy(result,
59095fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
59195fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
59295fa0405SHaiyang Zhang 	       query_complete->info_buflen);
59395fa0405SHaiyang Zhang 
59495fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
59595fa0405SHaiyang Zhang 
59695fa0405SHaiyang Zhang cleanup:
59795fa0405SHaiyang Zhang 	if (request)
59895fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
59995fa0405SHaiyang Zhang 
60095fa0405SHaiyang Zhang 	return ret;
60195fa0405SHaiyang Zhang }
60295fa0405SHaiyang Zhang 
60323312a3bSstephen hemminger /* Get the hardware offload capabilities */
60423312a3bSstephen hemminger static int
605867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device,
606867047c4Sstephen hemminger 		   struct ndis_offload *caps)
60723312a3bSstephen hemminger {
60823312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
60923312a3bSstephen hemminger 	int ret;
61023312a3bSstephen hemminger 
61123312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
61223312a3bSstephen hemminger 
613867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
61423312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
61523312a3bSstephen hemminger 					caps, &caps_len);
61623312a3bSstephen hemminger 	if (ret)
61723312a3bSstephen hemminger 		return ret;
61823312a3bSstephen hemminger 
61923312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
62023312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
62123312a3bSstephen hemminger 			    caps->header.type);
62223312a3bSstephen hemminger 		return -EINVAL;
62323312a3bSstephen hemminger 	}
62423312a3bSstephen hemminger 
62523312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
62623312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
62723312a3bSstephen hemminger 			    caps->header.revision);
62823312a3bSstephen hemminger 		return -EINVAL;
62923312a3bSstephen hemminger 	}
63023312a3bSstephen hemminger 
63123312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
63223312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
63323312a3bSstephen hemminger 		netdev_warn(dev->ndev,
63423312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
63523312a3bSstephen hemminger 			    caps->header.size, caps_len);
63623312a3bSstephen hemminger 		return -EINVAL;
63723312a3bSstephen hemminger 	}
63823312a3bSstephen hemminger 
63923312a3bSstephen hemminger 	return 0;
64023312a3bSstephen hemminger }
64123312a3bSstephen hemminger 
642867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev,
643867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
64495fa0405SHaiyang Zhang {
64595fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
64695fa0405SHaiyang Zhang 
647867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
64895fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
64995fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
65095fa0405SHaiyang Zhang }
65195fa0405SHaiyang Zhang 
6521ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
6531ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
6541ce09e89SHaiyang Zhang 
655867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev,
656867047c4Sstephen hemminger 				const char *mac)
6571ce09e89SHaiyang Zhang {
6581ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
6591ce09e89SHaiyang Zhang 	struct rndis_request *request;
6601ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
6611ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
6621ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
6631ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
6641ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6651ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6661ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
667999028ccSNicholas Mc Guire 	int ret;
6681ce09e89SHaiyang Zhang 
6691ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6701ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6711ce09e89SHaiyang Zhang 	if (!request)
6721ce09e89SHaiyang Zhang 		return -ENOMEM;
6731ce09e89SHaiyang Zhang 
6741ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6751ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6761ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6771ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6781ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6791ce09e89SHaiyang Zhang 
6801ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6811ce09e89SHaiyang Zhang 		set->info_buf_offset);
6821ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6831ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6841ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6851ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6861ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6871ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6881ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6891ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6901ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6911ce09e89SHaiyang Zhang 
6921ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6931ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6941ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6951ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6961ce09e89SHaiyang Zhang 	if (ret < 0)
6971ce09e89SHaiyang Zhang 		goto cleanup;
6981ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6991ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
7001ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
7011ce09e89SHaiyang Zhang 	if (ret < 0)
7021ce09e89SHaiyang Zhang 		goto cleanup;
7031ce09e89SHaiyang Zhang 
7041ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7051ce09e89SHaiyang Zhang 	if (ret != 0)
7061ce09e89SHaiyang Zhang 		goto cleanup;
7071ce09e89SHaiyang Zhang 
7085362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7095362855aSVitaly Kuznetsov 
7101ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
711867047c4Sstephen hemminger 	if (set_complete->status != RNDIS_STATUS_SUCCESS)
712867047c4Sstephen hemminger 		ret = -EIO;
7131ce09e89SHaiyang Zhang 
7141ce09e89SHaiyang Zhang cleanup:
7151ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
7161ce09e89SHaiyang Zhang 	return ret;
7171ce09e89SHaiyang Zhang }
7181ce09e89SHaiyang Zhang 
719da19fcd0SLad, Prabhakar static int
720426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
7219749fed5Sstephen hemminger 				struct netvsc_device *nvdev,
7224a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
7234a0e70aeSKY Srinivasan {
7244a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
7254a0e70aeSKY Srinivasan 	struct rndis_request *request;
7264a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
7274a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
7284a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
7294a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
730999028ccSNicholas Mc Guire 	int ret;
731af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
732af9893a3SKY Srinivasan 
733af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
734af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
735af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
736af9893a3SKY Srinivasan 		 * UDP checksum offload.
737af9893a3SKY Srinivasan 		 */
738af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
739af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
740af9893a3SKY Srinivasan 	}
7414a0e70aeSKY Srinivasan 
7424a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
7434a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7444a0e70aeSKY Srinivasan 	if (!request)
7454a0e70aeSKY Srinivasan 		return -ENOMEM;
7464a0e70aeSKY Srinivasan 
7474a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
7484a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
7494a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
7504a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
7514a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
7524a0e70aeSKY Srinivasan 
7534a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
7544a0e70aeSKY Srinivasan 				set->info_buf_offset);
7554a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
7564a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
7574a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
7584a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
7594a0e70aeSKY Srinivasan 
7604a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
7614a0e70aeSKY Srinivasan 	if (ret != 0)
7624a0e70aeSKY Srinivasan 		goto cleanup;
7634a0e70aeSKY Srinivasan 
7645362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7654a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7664a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
767af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7684a0e70aeSKY Srinivasan 			   set_complete->status);
7694a0e70aeSKY Srinivasan 		ret = -EINVAL;
7704a0e70aeSKY Srinivasan 	}
7714a0e70aeSKY Srinivasan 
7724a0e70aeSKY Srinivasan cleanup:
7734a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7744a0e70aeSKY Srinivasan 	return ret;
7754a0e70aeSKY Srinivasan }
7761ce09e89SHaiyang Zhang 
777962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
778715e2ec5SHaiyang Zhang 			       const u8 *rss_key)
7795b54dac8SHaiyang Zhang {
7803d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7815b54dac8SHaiyang Zhang 	struct rndis_request *request;
7825b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7835b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7845b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
785962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7865b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7875b54dac8SHaiyang Zhang 	u32 *itab;
7885b54dac8SHaiyang Zhang 	u8 *keyp;
789999028ccSNicholas Mc Guire 	int i, ret;
7905b54dac8SHaiyang Zhang 
7915b54dac8SHaiyang Zhang 	request = get_rndis_request(
7925b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7935b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7945b54dac8SHaiyang Zhang 	if (!request)
7955b54dac8SHaiyang Zhang 		return -ENOMEM;
7965b54dac8SHaiyang Zhang 
7975b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7985b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7995b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
8005b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
8015b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
8025b54dac8SHaiyang Zhang 
8035b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
8045b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
8055b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
8065b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
8075b54dac8SHaiyang Zhang 	rssp->flag = 0;
8085b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
8094c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
8104c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
8115b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
8125b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
813962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
814bfcbcb67SStephen Hemminger 	rssp->hashkey_offset = rssp->indirect_taboffset +
8155b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
8165b54dac8SHaiyang Zhang 
8175b54dac8SHaiyang Zhang 	/* Set indirection table entries */
8185b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
8195b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
82047371300SHaiyang Zhang 		itab[i] = rdev->rx_table[i];
8215b54dac8SHaiyang Zhang 
8225b54dac8SHaiyang Zhang 	/* Set hask key values */
823bfcbcb67SStephen Hemminger 	keyp = (u8 *)((unsigned long)rssp + rssp->hashkey_offset);
824962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
8255b54dac8SHaiyang Zhang 
8265b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
8275b54dac8SHaiyang Zhang 	if (ret != 0)
8285b54dac8SHaiyang Zhang 		goto cleanup;
8295b54dac8SHaiyang Zhang 
8305362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
8315b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
832962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
833962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
834962f3feeSstephen hemminger 	else {
8355b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
8365b54dac8SHaiyang Zhang 			   set_complete->status);
8375b54dac8SHaiyang Zhang 		ret = -EINVAL;
8385b54dac8SHaiyang Zhang 	}
8395b54dac8SHaiyang Zhang 
8405b54dac8SHaiyang Zhang cleanup:
8415b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
8425b54dac8SHaiyang Zhang 	return ret;
8435b54dac8SHaiyang Zhang }
8445b54dac8SHaiyang Zhang 
845867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev,
846867047c4Sstephen hemminger 						 struct netvsc_device *net_device)
84795fa0405SHaiyang Zhang {
84895fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
84995fa0405SHaiyang Zhang 	u32 link_status;
85095fa0405SHaiyang Zhang 
851867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
85295fa0405SHaiyang Zhang 					 RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
85395fa0405SHaiyang Zhang 					 &link_status, &size);
85495fa0405SHaiyang Zhang }
85595fa0405SHaiyang Zhang 
856867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev,
857867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
858b37879e6SHaiyang Zhang {
859b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
860b37879e6SHaiyang Zhang 	u32 link_speed;
861b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
862b37879e6SHaiyang Zhang 	int ret;
863b37879e6SHaiyang Zhang 
864867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
865867047c4Sstephen hemminger 					RNDIS_OID_GEN_LINK_SPEED,
866b37879e6SHaiyang Zhang 					&link_speed, &size);
867b37879e6SHaiyang Zhang 
868b37879e6SHaiyang Zhang 	if (!ret) {
869b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
870b37879e6SHaiyang Zhang 
871b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
872b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
873b37879e6SHaiyang Zhang 		 */
874b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
875b37879e6SHaiyang Zhang 	}
876b37879e6SHaiyang Zhang 
877b37879e6SHaiyang Zhang 	return ret;
878b37879e6SHaiyang Zhang }
879b37879e6SHaiyang Zhang 
8804f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
8814f19c0d8Sstephen hemminger 					  u32 new_filter)
88295fa0405SHaiyang Zhang {
88395fa0405SHaiyang Zhang 	struct rndis_request *request;
88495fa0405SHaiyang Zhang 	struct rndis_set_request *set;
885999028ccSNicholas Mc Guire 	int ret;
88695fa0405SHaiyang Zhang 
8877eeb4a6eSStephen Hemminger 	if (dev->filter == new_filter)
8887eeb4a6eSStephen Hemminger 		return 0;
8897eeb4a6eSStephen Hemminger 
89051491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
89195fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
89295fa0405SHaiyang Zhang 			sizeof(u32));
893ce12b810Sstephen hemminger 	if (!request)
894ce12b810Sstephen hemminger 		return -ENOMEM;
895ce12b810Sstephen hemminger 
89695fa0405SHaiyang Zhang 	/* Setup the rndis set */
89795fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
89895fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
89995fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
90095fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
90195fa0405SHaiyang Zhang 
90295fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
90395fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
90495fa0405SHaiyang Zhang 
90595fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
9067eeb4a6eSStephen Hemminger 	if (ret == 0) {
9075362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
9087eeb4a6eSStephen Hemminger 		dev->filter = new_filter;
9097eeb4a6eSStephen Hemminger 	}
91095fa0405SHaiyang Zhang 
91195fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
912ce12b810Sstephen hemminger 
91395fa0405SHaiyang Zhang 	return ret;
91495fa0405SHaiyang Zhang }
91595fa0405SHaiyang Zhang 
9164f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
9174f19c0d8Sstephen hemminger {
9184f19c0d8Sstephen hemminger 	struct rndis_device *rdev
9194f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
920009f766cSStephen Hemminger 	u32 filter = NDIS_PACKET_TYPE_DIRECTED;
921009f766cSStephen Hemminger 	unsigned int flags = rdev->ndev->flags;
9224f19c0d8Sstephen hemminger 
923009f766cSStephen Hemminger 	if (flags & IFF_PROMISC) {
924009f766cSStephen Hemminger 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
925009f766cSStephen Hemminger 	} else {
926f03dbb06SStephen Hemminger 		if (!netdev_mc_empty(rdev->ndev) || (flags & IFF_ALLMULTI))
927de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
928009f766cSStephen Hemminger 		if (flags & IFF_BROADCAST)
929de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_BROADCAST;
930009f766cSStephen Hemminger 	}
931009f766cSStephen Hemminger 
932009f766cSStephen Hemminger 	rndis_filter_set_packet_filter(rdev, filter);
9334f19c0d8Sstephen hemminger }
9344f19c0d8Sstephen hemminger 
9354f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
9364f19c0d8Sstephen hemminger {
9374f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
9384f19c0d8Sstephen hemminger 
9394f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
9404f19c0d8Sstephen hemminger }
9414f19c0d8Sstephen hemminger 
942867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev,
943867047c4Sstephen hemminger 				    struct netvsc_device *nvdev)
94495fa0405SHaiyang Zhang {
94595fa0405SHaiyang Zhang 	struct rndis_request *request;
94695fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
94795fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
94895fa0405SHaiyang Zhang 	u32 status;
949999028ccSNicholas Mc Guire 	int ret;
95095fa0405SHaiyang Zhang 
95151491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
95295fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
95395fa0405SHaiyang Zhang 	if (!request) {
95495fa0405SHaiyang Zhang 		ret = -ENOMEM;
95595fa0405SHaiyang Zhang 		goto cleanup;
95695fa0405SHaiyang Zhang 	}
95795fa0405SHaiyang Zhang 
95895fa0405SHaiyang Zhang 	/* Setup the rndis set */
95995fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
96095fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
96195fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
962fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
96395fa0405SHaiyang Zhang 
96495fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
96595fa0405SHaiyang Zhang 
96695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
96795fa0405SHaiyang Zhang 	if (ret != 0) {
96895fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
96995fa0405SHaiyang Zhang 		goto cleanup;
97095fa0405SHaiyang Zhang 	}
97195fa0405SHaiyang Zhang 
9725362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
97395fa0405SHaiyang Zhang 
97495fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
97595fa0405SHaiyang Zhang 	status = init_complete->status;
97695fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
97795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
9787c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
9797c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
98095fa0405SHaiyang Zhang 		ret = 0;
98195fa0405SHaiyang Zhang 	} else {
98295fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
98395fa0405SHaiyang Zhang 		ret = -EINVAL;
98495fa0405SHaiyang Zhang 	}
98595fa0405SHaiyang Zhang 
98695fa0405SHaiyang Zhang cleanup:
98795fa0405SHaiyang Zhang 	if (request)
98895fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
98995fa0405SHaiyang Zhang 
99095fa0405SHaiyang Zhang 	return ret;
99195fa0405SHaiyang Zhang }
99295fa0405SHaiyang Zhang 
99346b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
99446b4f7f5Sstephen hemminger {
99546b4f7f5Sstephen hemminger 	int i;
99646b4f7f5Sstephen hemminger 
99746b4f7f5Sstephen hemminger 	for (i = 0; i < nvdev->num_chn; i++) {
99846b4f7f5Sstephen hemminger 		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
99946b4f7f5Sstephen hemminger 
10007426b1a5Sstephen hemminger 		if (nvchan->mrc.first != nvchan->mrc.next)
10017426b1a5Sstephen hemminger 			return false;
10027426b1a5Sstephen hemminger 
100346b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
100446b4f7f5Sstephen hemminger 			return false;
100546b4f7f5Sstephen hemminger 	}
100646b4f7f5Sstephen hemminger 
100746b4f7f5Sstephen hemminger 	return true;
100846b4f7f5Sstephen hemminger }
100946b4f7f5Sstephen hemminger 
10100e96460eSStephen Hemminger static void rndis_filter_halt_device(struct netvsc_device *nvdev,
10110e96460eSStephen Hemminger 				     struct rndis_device *dev)
101295fa0405SHaiyang Zhang {
101395fa0405SHaiyang Zhang 	struct rndis_request *request;
101495fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
101595fa0405SHaiyang Zhang 
101695fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
101751491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
101895fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
101995fa0405SHaiyang Zhang 	if (!request)
102095fa0405SHaiyang Zhang 		goto cleanup;
102195fa0405SHaiyang Zhang 
102295fa0405SHaiyang Zhang 	/* Setup the rndis set */
102395fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
102495fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
102595fa0405SHaiyang Zhang 
102695fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
102795fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
102895fa0405SHaiyang Zhang 
102995fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
103095fa0405SHaiyang Zhang 
103195fa0405SHaiyang Zhang cleanup:
1032ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
103300ecfb3bSstephen hemminger 
103400ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
103500ecfb3bSstephen hemminger 	wmb();
1036ae9e63bbSHaiyang Zhang 
1037ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
103846b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
1039ae9e63bbSHaiyang Zhang 
104095fa0405SHaiyang Zhang 	if (request)
104195fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
104295fa0405SHaiyang Zhang }
104395fa0405SHaiyang Zhang 
104495fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
104595fa0405SHaiyang Zhang {
104695fa0405SHaiyang Zhang 	int ret;
104795fa0405SHaiyang Zhang 
104895fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
104995fa0405SHaiyang Zhang 		return 0;
105095fa0405SHaiyang Zhang 
105195fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
105295fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
105395fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
105495fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
105595fa0405SHaiyang Zhang 	if (ret == 0)
105695fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
105795fa0405SHaiyang Zhang 
105895fa0405SHaiyang Zhang 	return ret;
105995fa0405SHaiyang Zhang }
106095fa0405SHaiyang Zhang 
106195fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
106295fa0405SHaiyang Zhang {
106395fa0405SHaiyang Zhang 	int ret;
106495fa0405SHaiyang Zhang 
106595fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
106695fa0405SHaiyang Zhang 		return 0;
106795fa0405SHaiyang Zhang 
10684f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
10694f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
10704f19c0d8Sstephen hemminger 
107195fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1072c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1073c3582a2cSHaiyang Zhang 		ret = 0;
1074c3582a2cSHaiyang Zhang 
107595fa0405SHaiyang Zhang 	if (ret == 0)
107695fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
107795fa0405SHaiyang Zhang 
107895fa0405SHaiyang Zhang 	return ret;
107995fa0405SHaiyang Zhang }
108095fa0405SHaiyang Zhang 
10815b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
10825b54dac8SHaiyang Zhang {
10833d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
10843d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
1085867047c4Sstephen hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
1086867047c4Sstephen hemminger 	struct netvsc_device *nvscdev;
10875b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
10886de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
10896de38af6Sstephen hemminger 	int ret;
10905b54dac8SHaiyang Zhang 
1091867047c4Sstephen hemminger 	/* This is safe because this callback only happens when
1092867047c4Sstephen hemminger 	 * new device is being setup and waiting on the channel_init_wait.
1093867047c4Sstephen hemminger 	 */
1094867047c4Sstephen hemminger 	nvscdev = rcu_dereference_raw(ndev_ctx->nvdev);
1095867047c4Sstephen hemminger 	if (!nvscdev || chn_index >= nvscdev->num_chn)
10965b54dac8SHaiyang Zhang 		return;
10975b54dac8SHaiyang Zhang 
10986de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
10996de38af6Sstephen hemminger 
1100b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1101b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1102b1dd90ceSK. Y. Srinivasan 	 */
1103b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1104b1dd90ceSK. Y. Srinivasan 
1105bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1106bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
1107bffb1842SK. Y. Srinivasan 
1108a7f99d0fSStephen Hemminger 	ret = vmbus_open(new_sc, netvsc_ring_bytes,
1109a7f99d0fSStephen Hemminger 			 netvsc_ring_bytes, NULL, 0,
11106de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
111176bb5db5Sstephen hemminger 	if (ret == 0)
11126de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
111376bb5db5Sstephen hemminger 	else
11148195b139SStephen Hemminger 		netdev_notice(ndev, "sub channel open failed: %d\n", ret);
111515a863bfSstephen hemminger 
11168f2bb1deSStephen Hemminger 	if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn)
1117732e4985Sstephen hemminger 		wake_up(&nvscdev->subchan_open);
11185b54dac8SHaiyang Zhang }
11195b54dac8SHaiyang Zhang 
11208195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe.
11218195b139SStephen Hemminger  * This breaks overlap of processing the host message for the
11228195b139SStephen Hemminger  * new primary channel with the initialization of sub-channels.
11238195b139SStephen Hemminger  */
11243ffe64f1SStephen Hemminger int rndis_set_subchannel(struct net_device *ndev, struct netvsc_device *nvdev)
11258195b139SStephen Hemminger {
11268195b139SStephen Hemminger 	struct nvsp_message *init_packet = &nvdev->channel_init_pkt;
11273ffe64f1SStephen Hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
11283ffe64f1SStephen Hemminger 	struct hv_device *hv_dev = ndev_ctx->device_ctx;
11293ffe64f1SStephen Hemminger 	struct rndis_device *rdev = nvdev->extension;
11308195b139SStephen Hemminger 	int i, ret;
11318195b139SStephen Hemminger 
11323ffe64f1SStephen Hemminger 	ASSERT_RTNL();
11338195b139SStephen Hemminger 
11348195b139SStephen Hemminger 	memset(init_packet, 0, sizeof(struct nvsp_message));
11358195b139SStephen Hemminger 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
11368195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
11378195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
11388195b139SStephen Hemminger 						nvdev->num_chn - 1;
1139ec966381SStephen Hemminger 	trace_nvsp_send(ndev, init_packet);
1140ec966381SStephen Hemminger 
11418195b139SStephen Hemminger 	ret = vmbus_sendpacket(hv_dev->channel, init_packet,
11428195b139SStephen Hemminger 			       sizeof(struct nvsp_message),
11438195b139SStephen Hemminger 			       (unsigned long)init_packet,
11448195b139SStephen Hemminger 			       VM_PKT_DATA_INBAND,
11458195b139SStephen Hemminger 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
11468195b139SStephen Hemminger 	if (ret) {
11478195b139SStephen Hemminger 		netdev_err(ndev, "sub channel allocate send failed: %d\n", ret);
11483ffe64f1SStephen Hemminger 		return ret;
11498195b139SStephen Hemminger 	}
11508195b139SStephen Hemminger 
11518195b139SStephen Hemminger 	wait_for_completion(&nvdev->channel_init_wait);
11528195b139SStephen Hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
11538195b139SStephen Hemminger 		netdev_err(ndev, "sub channel request failed\n");
11543ffe64f1SStephen Hemminger 		return -EIO;
11558195b139SStephen Hemminger 	}
11568195b139SStephen Hemminger 
11578195b139SStephen Hemminger 	nvdev->num_chn = 1 +
11588195b139SStephen Hemminger 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
11598195b139SStephen Hemminger 
11608195b139SStephen Hemminger 	/* wait for all sub channels to open */
11618195b139SStephen Hemminger 	wait_event(nvdev->subchan_open,
11628195b139SStephen Hemminger 		   atomic_read(&nvdev->open_chn) == nvdev->num_chn);
11638195b139SStephen Hemminger 
11648195b139SStephen Hemminger 	/* ignore failues from setting rss parameters, still have channels */
11658195b139SStephen Hemminger 	rndis_filter_set_rss_param(rdev, netvsc_hash_key);
11668195b139SStephen Hemminger 
11678195b139SStephen Hemminger 	netif_set_real_num_tx_queues(ndev, nvdev->num_chn);
11688195b139SStephen Hemminger 	netif_set_real_num_rx_queues(ndev, nvdev->num_chn);
11698195b139SStephen Hemminger 
1170a6fb6aa3SHaiyang Zhang 	for (i = 0; i < VRSS_SEND_TAB_SIZE; i++)
1171a6fb6aa3SHaiyang Zhang 		ndev_ctx->tx_table[i] = i % nvdev->num_chn;
1172a6fb6aa3SHaiyang Zhang 
11733ffe64f1SStephen Hemminger 	return 0;
11748195b139SStephen Hemminger }
11758195b139SStephen Hemminger 
1176aefd80e8SVitaly Kuznetsov static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device,
1177aefd80e8SVitaly Kuznetsov 				   struct netvsc_device *nvdev)
117895fa0405SHaiyang Zhang {
1179aefd80e8SVitaly Kuznetsov 	struct net_device *net = rndis_device->ndev;
11803d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
118123312a3bSstephen hemminger 	struct ndis_offload hwcaps;
11824a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
118323312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
1184aefd80e8SVitaly Kuznetsov 	int ret;
118595fa0405SHaiyang Zhang 
118623312a3bSstephen hemminger 	/* Find HW offload capabilities */
1187aefd80e8SVitaly Kuznetsov 	ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps);
11889749fed5Sstephen hemminger 	if (ret != 0)
1189aefd80e8SVitaly Kuznetsov 		return ret;
119023312a3bSstephen hemminger 
119123312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
11924a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
119323312a3bSstephen hemminger 
119423312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
119523312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
119623312a3bSstephen hemminger 
1197aefd80e8SVitaly Kuznetsov 	/* Reset previously set hw_features flags */
1198aefd80e8SVitaly Kuznetsov 	net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES;
1199aefd80e8SVitaly Kuznetsov 	net_device_ctx->tx_checksum_mask = 0;
1200aefd80e8SVitaly Kuznetsov 
120123312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
1202aefd80e8SVitaly Kuznetsov 	net->hw_features |= NETIF_F_RXCSUM;
120323312a3bSstephen hemminger 
120423312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
120523312a3bSstephen hemminger 		/* Can checksum TCP */
120623312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
120723312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
120823312a3bSstephen hemminger 
12094a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
121023312a3bSstephen hemminger 
121123312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
12124a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
121323312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
121423312a3bSstephen hemminger 
121523312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
121623312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
121723312a3bSstephen hemminger 		}
121823312a3bSstephen hemminger 
121923312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
122023312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
122123312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
122223312a3bSstephen hemminger 		}
122323312a3bSstephen hemminger 	}
122423312a3bSstephen hemminger 
122523312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
122623312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
122723312a3bSstephen hemminger 
122823312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
122923312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
123023312a3bSstephen hemminger 
123123312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
123223312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
123323312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
123423312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
123523312a3bSstephen hemminger 
123623312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
123723312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
123823312a3bSstephen hemminger 		}
123923312a3bSstephen hemminger 
124023312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
124123312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
124223312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
124323312a3bSstephen hemminger 		}
124423312a3bSstephen hemminger 	}
124523312a3bSstephen hemminger 
1246c8e4eff4SHaiyang Zhang 	if (hwcaps.rsc.ip4 && hwcaps.rsc.ip6) {
1247c8e4eff4SHaiyang Zhang 		net->hw_features |= NETIF_F_LRO;
1248c8e4eff4SHaiyang Zhang 
1249c8e4eff4SHaiyang Zhang 		offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
1250c8e4eff4SHaiyang Zhang 		offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
1251c8e4eff4SHaiyang Zhang 	}
1252c8e4eff4SHaiyang Zhang 
1253aefd80e8SVitaly Kuznetsov 	/* In case some hw_features disappeared we need to remove them from
1254aefd80e8SVitaly Kuznetsov 	 * net->features list as they're no longer supported.
1255aefd80e8SVitaly Kuznetsov 	 */
1256aefd80e8SVitaly Kuznetsov 	net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features;
1257aefd80e8SVitaly Kuznetsov 
125823312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
12594a0e70aeSKY Srinivasan 
1260aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, nvdev, &offloads);
1261aefd80e8SVitaly Kuznetsov 
1262aefd80e8SVitaly Kuznetsov 	return ret;
1263aefd80e8SVitaly Kuznetsov }
1264aefd80e8SVitaly Kuznetsov 
12650fe554a4SStephen Hemminger static void rndis_get_friendly_name(struct net_device *net,
12660fe554a4SStephen Hemminger 				    struct rndis_device *rndis_device,
12670fe554a4SStephen Hemminger 				    struct netvsc_device *net_device)
12680fe554a4SStephen Hemminger {
12690fe554a4SStephen Hemminger 	ucs2_char_t wname[256];
12700fe554a4SStephen Hemminger 	unsigned long len;
12710fe554a4SStephen Hemminger 	u8 ifalias[256];
12720fe554a4SStephen Hemminger 	u32 size;
12730fe554a4SStephen Hemminger 
12740fe554a4SStephen Hemminger 	size = sizeof(wname);
12750fe554a4SStephen Hemminger 	if (rndis_filter_query_device(rndis_device, net_device,
12760fe554a4SStephen Hemminger 				      RNDIS_OID_GEN_FRIENDLY_NAME,
12770fe554a4SStephen Hemminger 				      wname, &size) != 0)
1278d97cde6aSStephen Hemminger 		return;	/* ignore if host does not support */
1279d97cde6aSStephen Hemminger 
1280d97cde6aSStephen Hemminger 	if (size == 0)
1281d97cde6aSStephen Hemminger 		return;	/* name not set */
12820fe554a4SStephen Hemminger 
12830fe554a4SStephen Hemminger 	/* Convert Windows Unicode string to UTF-8 */
12840fe554a4SStephen Hemminger 	len = ucs2_as_utf8(ifalias, wname, sizeof(ifalias));
12850fe554a4SStephen Hemminger 
12860fe554a4SStephen Hemminger 	/* ignore the default value from host */
12870fe554a4SStephen Hemminger 	if (strcmp(ifalias, "Network Adapter") != 0)
12880fe554a4SStephen Hemminger 		dev_set_alias(net, ifalias, len);
12890fe554a4SStephen Hemminger }
12900fe554a4SStephen Hemminger 
1291aefd80e8SVitaly Kuznetsov struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
1292aefd80e8SVitaly Kuznetsov 				      struct netvsc_device_info *device_info)
1293aefd80e8SVitaly Kuznetsov {
1294aefd80e8SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
1295aefd80e8SVitaly Kuznetsov 	struct netvsc_device *net_device;
1296aefd80e8SVitaly Kuznetsov 	struct rndis_device *rndis_device;
1297aefd80e8SVitaly Kuznetsov 	struct ndis_recv_scale_cap rsscap;
1298aefd80e8SVitaly Kuznetsov 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
1299aefd80e8SVitaly Kuznetsov 	u32 mtu, size;
1300aefd80e8SVitaly Kuznetsov 	u32 num_possible_rss_qs;
1301aefd80e8SVitaly Kuznetsov 	int i, ret;
1302aefd80e8SVitaly Kuznetsov 
1303aefd80e8SVitaly Kuznetsov 	rndis_device = get_rndis_device();
1304aefd80e8SVitaly Kuznetsov 	if (!rndis_device)
1305aefd80e8SVitaly Kuznetsov 		return ERR_PTR(-ENODEV);
1306aefd80e8SVitaly Kuznetsov 
1307aefd80e8SVitaly Kuznetsov 	/* Let the inner driver handle this first to create the netvsc channel
1308aefd80e8SVitaly Kuznetsov 	 * NOTE! Once the channel is created, we may get a receive callback
1309aefd80e8SVitaly Kuznetsov 	 * (RndisFilterOnReceive()) before this call is completed
1310aefd80e8SVitaly Kuznetsov 	 */
1311aefd80e8SVitaly Kuznetsov 	net_device = netvsc_device_add(dev, device_info);
1312aefd80e8SVitaly Kuznetsov 	if (IS_ERR(net_device)) {
1313aefd80e8SVitaly Kuznetsov 		kfree(rndis_device);
1314aefd80e8SVitaly Kuznetsov 		return net_device;
1315aefd80e8SVitaly Kuznetsov 	}
1316aefd80e8SVitaly Kuznetsov 
1317aefd80e8SVitaly Kuznetsov 	/* Initialize the rndis device */
1318aefd80e8SVitaly Kuznetsov 	net_device->max_chn = 1;
1319aefd80e8SVitaly Kuznetsov 	net_device->num_chn = 1;
1320aefd80e8SVitaly Kuznetsov 
1321aefd80e8SVitaly Kuznetsov 	net_device->extension = rndis_device;
1322aefd80e8SVitaly Kuznetsov 	rndis_device->ndev = net;
1323aefd80e8SVitaly Kuznetsov 
1324aefd80e8SVitaly Kuznetsov 	/* Send the rndis initialization message */
1325aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_init_device(rndis_device, net_device);
1326aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1327aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1328aefd80e8SVitaly Kuznetsov 
1329aefd80e8SVitaly Kuznetsov 	/* Get the MTU from the host */
1330aefd80e8SVitaly Kuznetsov 	size = sizeof(u32);
1331aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device(rndis_device, net_device,
1332aefd80e8SVitaly Kuznetsov 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
1333aefd80e8SVitaly Kuznetsov 					&mtu, &size);
1334aefd80e8SVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
1335aefd80e8SVitaly Kuznetsov 		net->mtu = mtu;
1336aefd80e8SVitaly Kuznetsov 
1337aefd80e8SVitaly Kuznetsov 	/* Get the mac address */
1338aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device_mac(rndis_device, net_device);
1339aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1340aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1341aefd80e8SVitaly Kuznetsov 
1342aefd80e8SVitaly Kuznetsov 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
1343aefd80e8SVitaly Kuznetsov 
13440fe554a4SStephen Hemminger 	/* Get friendly name as ifalias*/
13450fe554a4SStephen Hemminger 	if (!net->ifalias)
13460fe554a4SStephen Hemminger 		rndis_get_friendly_name(net, rndis_device, net_device);
13470fe554a4SStephen Hemminger 
1348aefd80e8SVitaly Kuznetsov 	/* Query and set hardware capabilities */
1349aefd80e8SVitaly Kuznetsov 	ret = rndis_netdev_set_hwcaps(rndis_device, net_device);
1350aefd80e8SVitaly Kuznetsov 	if (ret != 0)
13514a0e70aeSKY Srinivasan 		goto err_dev_remv;
13524a0e70aeSKY Srinivasan 
1353867047c4Sstephen hemminger 	rndis_filter_query_device_link_status(rndis_device, net_device);
135495fa0405SHaiyang Zhang 
135593ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
135695fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1357dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
135895fa0405SHaiyang Zhang 
13595b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
136055be9f25SMohammed Gamal 		goto out;
13615b54dac8SHaiyang Zhang 
1362867047c4Sstephen hemminger 	rndis_filter_query_link_speed(rndis_device, net_device);
1363b37879e6SHaiyang Zhang 
13645b54dac8SHaiyang Zhang 	/* vRSS setup */
13655b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
1366867047c4Sstephen hemminger 	ret = rndis_filter_query_device(rndis_device, net_device,
13675b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
13685b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
13695b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
13705b54dac8SHaiyang Zhang 		goto out;
13715b54dac8SHaiyang Zhang 
137225a39f7fSHaiyang Zhang 	/* This guarantees that num_possible_rss_qs <= num_online_cpus */
137325a39f7fSHaiyang Zhang 	num_possible_rss_qs = min_t(u32, num_online_cpus(),
13743071ada4Sstephen hemminger 				    rsscap.num_recv_que);
13753071ada4Sstephen hemminger 
13763071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
13778ebdcc52SAndrew Schwartzmeyer 
13788ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
13793071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1380ff4a4419Sstephen hemminger 
1381ff4a4419Sstephen hemminger 	for (i = 0; i < ITAB_NUM; i++)
138247371300SHaiyang Zhang 		rndis_device->rx_table[i] = ethtool_rxfh_indir_default(
138347371300SHaiyang Zhang 						i, net_device->num_chn);
1384ff4a4419Sstephen hemminger 
1385732e4985Sstephen hemminger 	atomic_set(&net_device->open_chn, 1);
13868195b139SStephen Hemminger 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
13875b54dac8SHaiyang Zhang 
13887426b1a5Sstephen hemminger 	for (i = 1; i < net_device->num_chn; i++) {
13897426b1a5Sstephen hemminger 		ret = netvsc_alloc_recv_comp_ring(net_device, i);
13907426b1a5Sstephen hemminger 		if (ret) {
13917426b1a5Sstephen hemminger 			while (--i != 0)
13927426b1a5Sstephen hemminger 				vfree(net_device->chan_table[i].mrc.slots);
13937426b1a5Sstephen hemminger 			goto out;
13947426b1a5Sstephen hemminger 		}
13957426b1a5Sstephen hemminger 	}
13967426b1a5Sstephen hemminger 
13978195b139SStephen Hemminger 	for (i = 1; i < net_device->num_chn; i++)
13988195b139SStephen Hemminger 		netif_napi_add(net, &net_device->chan_table[i].napi,
13998195b139SStephen Hemminger 			       netvsc_poll, NAPI_POLL_WEIGHT);
14005b54dac8SHaiyang Zhang 
14013ffe64f1SStephen Hemminger 	return net_device;
14025362855aSVitaly Kuznetsov 
14035b54dac8SHaiyang Zhang out:
14043ffe64f1SStephen Hemminger 	/* setting up multiple channels failed */
140559995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
14065b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
1407b19b4634STakashi Iwai 	return net_device;
14084a0e70aeSKY Srinivasan 
14094a0e70aeSKY Srinivasan err_dev_remv:
14102289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
14119749fed5Sstephen hemminger 	return ERR_PTR(ret);
141295fa0405SHaiyang Zhang }
141395fa0405SHaiyang Zhang 
14142289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
14152289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
141695fa0405SHaiyang Zhang {
141795fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1418d66ab514SHaiyang Zhang 
141995fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
14200e96460eSStephen Hemminger 	rndis_filter_halt_device(net_dev, rndis_dev);
142195fa0405SHaiyang Zhang 
142295fa0405SHaiyang Zhang 	net_dev->extension = NULL;
142395fa0405SHaiyang Zhang 
142495fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
142595fa0405SHaiyang Zhang }
142695fa0405SHaiyang Zhang 
14272f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
142895fa0405SHaiyang Zhang {
14292f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
143095fa0405SHaiyang Zhang 		return -EINVAL;
143195fa0405SHaiyang Zhang 
14322f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
143395fa0405SHaiyang Zhang }
143495fa0405SHaiyang Zhang 
14352f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
143695fa0405SHaiyang Zhang {
14375fccab3bSHaiyang Zhang 	if (!nvdev)
143895fa0405SHaiyang Zhang 		return -EINVAL;
143995fa0405SHaiyang Zhang 
14405fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
144195fa0405SHaiyang Zhang }
1442