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 34a3a6cab5SHaiyang Zhang #define RNDIS_EXT_LEN 100 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 9795fa0405SHaiyang Zhang /* 9895fa0405SHaiyang Zhang * Set the request id. This field is always after the rndis header for 9995fa0405SHaiyang Zhang * request/response packet types so we just used the SetRequest as a 10095fa0405SHaiyang Zhang * template 10195fa0405SHaiyang Zhang */ 10295fa0405SHaiyang Zhang set = &rndis_msg->msg.set_req; 10395fa0405SHaiyang Zhang set->req_id = atomic_inc_return(&dev->new_req_id); 10495fa0405SHaiyang Zhang 10595fa0405SHaiyang Zhang /* Add to the request list */ 10695fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 10795fa0405SHaiyang Zhang list_add_tail(&request->list_ent, &dev->req_list); 10895fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 10995fa0405SHaiyang Zhang 11095fa0405SHaiyang Zhang return request; 11195fa0405SHaiyang Zhang } 11295fa0405SHaiyang Zhang 11395fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev, 11495fa0405SHaiyang Zhang struct rndis_request *req) 11595fa0405SHaiyang Zhang { 11695fa0405SHaiyang Zhang unsigned long flags; 11795fa0405SHaiyang Zhang 11895fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 11995fa0405SHaiyang Zhang list_del(&req->list_ent); 12095fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 12195fa0405SHaiyang Zhang 12295fa0405SHaiyang Zhang kfree(req); 12395fa0405SHaiyang Zhang } 12495fa0405SHaiyang Zhang 12595fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev, 12695fa0405SHaiyang Zhang struct rndis_message *rndis_msg) 12795fa0405SHaiyang Zhang { 12895fa0405SHaiyang Zhang struct net_device *netdev; 12995fa0405SHaiyang Zhang struct netvsc_device *net_device; 13095fa0405SHaiyang Zhang 13195fa0405SHaiyang Zhang net_device = hv_get_drvdata(hv_dev); 13295fa0405SHaiyang Zhang netdev = net_device->ndev; 13395fa0405SHaiyang Zhang 13495fa0405SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 13551491167SLinus Walleij case RNDIS_MSG_PACKET: 13651491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, " 13795fa0405SHaiyang Zhang "data offset %u data len %u, # oob %u, " 13895fa0405SHaiyang Zhang "oob offset %u, oob len %u, pkt offset %u, " 13995fa0405SHaiyang Zhang "pkt len %u\n", 14095fa0405SHaiyang Zhang rndis_msg->msg_len, 14195fa0405SHaiyang Zhang rndis_msg->msg.pkt.data_offset, 14295fa0405SHaiyang Zhang rndis_msg->msg.pkt.data_len, 14395fa0405SHaiyang Zhang rndis_msg->msg.pkt.num_oob_data_elements, 14495fa0405SHaiyang Zhang rndis_msg->msg.pkt.oob_data_offset, 14595fa0405SHaiyang Zhang rndis_msg->msg.pkt.oob_data_len, 14695fa0405SHaiyang Zhang rndis_msg->msg.pkt.per_pkt_info_offset, 14795fa0405SHaiyang Zhang rndis_msg->msg.pkt.per_pkt_info_len); 14895fa0405SHaiyang Zhang break; 14995fa0405SHaiyang Zhang 15051491167SLinus Walleij case RNDIS_MSG_INIT_C: 15151491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_INIT_C " 15295fa0405SHaiyang Zhang "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " 15395fa0405SHaiyang Zhang "device flags %d, max xfer size 0x%x, max pkts %u, " 15495fa0405SHaiyang Zhang "pkt aligned %u)\n", 15595fa0405SHaiyang Zhang rndis_msg->msg_len, 15695fa0405SHaiyang Zhang rndis_msg->msg.init_complete.req_id, 15795fa0405SHaiyang Zhang rndis_msg->msg.init_complete.status, 15895fa0405SHaiyang Zhang rndis_msg->msg.init_complete.major_ver, 15995fa0405SHaiyang Zhang rndis_msg->msg.init_complete.minor_ver, 16095fa0405SHaiyang Zhang rndis_msg->msg.init_complete.dev_flags, 16195fa0405SHaiyang Zhang rndis_msg->msg.init_complete.max_xfer_size, 16295fa0405SHaiyang Zhang rndis_msg->msg.init_complete. 16395fa0405SHaiyang Zhang max_pkt_per_msg, 16495fa0405SHaiyang Zhang rndis_msg->msg.init_complete. 16595fa0405SHaiyang Zhang pkt_alignment_factor); 16695fa0405SHaiyang Zhang break; 16795fa0405SHaiyang Zhang 16851491167SLinus Walleij case RNDIS_MSG_QUERY_C: 16951491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_QUERY_C " 17095fa0405SHaiyang Zhang "(len %u, id 0x%x, status 0x%x, buf len %u, " 17195fa0405SHaiyang Zhang "buf offset %u)\n", 17295fa0405SHaiyang Zhang rndis_msg->msg_len, 17395fa0405SHaiyang Zhang rndis_msg->msg.query_complete.req_id, 17495fa0405SHaiyang Zhang rndis_msg->msg.query_complete.status, 17595fa0405SHaiyang Zhang rndis_msg->msg.query_complete. 17695fa0405SHaiyang Zhang info_buflen, 17795fa0405SHaiyang Zhang rndis_msg->msg.query_complete. 17895fa0405SHaiyang Zhang info_buf_offset); 17995fa0405SHaiyang Zhang break; 18095fa0405SHaiyang Zhang 18151491167SLinus Walleij case RNDIS_MSG_SET_C: 18295fa0405SHaiyang Zhang netdev_dbg(netdev, 18351491167SLinus Walleij "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n", 18495fa0405SHaiyang Zhang rndis_msg->msg_len, 18595fa0405SHaiyang Zhang rndis_msg->msg.set_complete.req_id, 18695fa0405SHaiyang Zhang rndis_msg->msg.set_complete.status); 18795fa0405SHaiyang Zhang break; 18895fa0405SHaiyang Zhang 18951491167SLinus Walleij case RNDIS_MSG_INDICATE: 19051491167SLinus Walleij netdev_dbg(netdev, "RNDIS_MSG_INDICATE " 19195fa0405SHaiyang Zhang "(len %u, status 0x%x, buf len %u, buf offset %u)\n", 19295fa0405SHaiyang Zhang rndis_msg->msg_len, 19395fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status, 19495fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status_buflen, 19595fa0405SHaiyang Zhang rndis_msg->msg.indicate_status.status_buf_offset); 19695fa0405SHaiyang Zhang break; 19795fa0405SHaiyang Zhang 19895fa0405SHaiyang Zhang default: 19995fa0405SHaiyang Zhang netdev_dbg(netdev, "0x%x (len %u)\n", 20095fa0405SHaiyang Zhang rndis_msg->ndis_msg_type, 20195fa0405SHaiyang Zhang rndis_msg->msg_len); 20295fa0405SHaiyang Zhang break; 20395fa0405SHaiyang Zhang } 20495fa0405SHaiyang Zhang } 20595fa0405SHaiyang Zhang 20695fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev, 20795fa0405SHaiyang Zhang struct rndis_request *req) 20895fa0405SHaiyang Zhang { 20995fa0405SHaiyang Zhang int ret; 21095fa0405SHaiyang Zhang struct hv_netvsc_packet *packet; 21195fa0405SHaiyang Zhang 21295fa0405SHaiyang Zhang /* Setup the packet to send it */ 21395fa0405SHaiyang Zhang packet = &req->pkt; 21495fa0405SHaiyang Zhang 21595fa0405SHaiyang Zhang packet->is_data_pkt = false; 21695fa0405SHaiyang Zhang packet->total_data_buflen = req->request_msg.msg_len; 21795fa0405SHaiyang Zhang packet->page_buf_cnt = 1; 21895fa0405SHaiyang Zhang 21995fa0405SHaiyang Zhang packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >> 22095fa0405SHaiyang Zhang PAGE_SHIFT; 22195fa0405SHaiyang Zhang packet->page_buf[0].len = req->request_msg.msg_len; 22295fa0405SHaiyang Zhang packet->page_buf[0].offset = 22395fa0405SHaiyang Zhang (unsigned long)&req->request_msg & (PAGE_SIZE - 1); 22495fa0405SHaiyang Zhang 22599e3fcfaSHaiyang Zhang /* Add one page_buf when request_msg crossing page boundary */ 22699e3fcfaSHaiyang Zhang if (packet->page_buf[0].offset + packet->page_buf[0].len > PAGE_SIZE) { 22799e3fcfaSHaiyang Zhang packet->page_buf_cnt++; 22899e3fcfaSHaiyang Zhang packet->page_buf[0].len = PAGE_SIZE - 22999e3fcfaSHaiyang Zhang packet->page_buf[0].offset; 23099e3fcfaSHaiyang Zhang packet->page_buf[1].pfn = virt_to_phys((void *)&req->request_msg 23199e3fcfaSHaiyang Zhang + packet->page_buf[0].len) >> PAGE_SHIFT; 23299e3fcfaSHaiyang Zhang packet->page_buf[1].offset = 0; 23399e3fcfaSHaiyang Zhang packet->page_buf[1].len = req->request_msg.msg_len - 23499e3fcfaSHaiyang Zhang packet->page_buf[0].len; 23599e3fcfaSHaiyang Zhang } 23699e3fcfaSHaiyang Zhang 237f1ea3cd7SHaiyang Zhang packet->completion.send.send_completion = NULL; 23895fa0405SHaiyang Zhang 23995fa0405SHaiyang Zhang ret = netvsc_send(dev->net_dev->dev, packet); 24095fa0405SHaiyang Zhang return ret; 24195fa0405SHaiyang Zhang } 24295fa0405SHaiyang Zhang 2431b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev, 2441b07da51SHaiyang Zhang struct rndis_request *request) 2451b07da51SHaiyang Zhang { 2461b07da51SHaiyang Zhang u32 link_status; 2471b07da51SHaiyang Zhang struct rndis_query_complete *query_complete; 2481b07da51SHaiyang Zhang 2491b07da51SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 2501b07da51SHaiyang Zhang 2511b07da51SHaiyang Zhang if (query_complete->status == RNDIS_STATUS_SUCCESS && 2521b07da51SHaiyang Zhang query_complete->info_buflen == sizeof(u32)) { 2531b07da51SHaiyang Zhang memcpy(&link_status, (void *)((unsigned long)query_complete + 2541b07da51SHaiyang Zhang query_complete->info_buf_offset), sizeof(u32)); 2551b07da51SHaiyang Zhang rdev->link_state = link_status != 0; 2561b07da51SHaiyang Zhang } 2571b07da51SHaiyang Zhang } 2581b07da51SHaiyang Zhang 25995fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev, 26095fa0405SHaiyang Zhang struct rndis_message *resp) 26195fa0405SHaiyang Zhang { 26295fa0405SHaiyang Zhang struct rndis_request *request = NULL; 26395fa0405SHaiyang Zhang bool found = false; 26495fa0405SHaiyang Zhang unsigned long flags; 26595fa0405SHaiyang Zhang struct net_device *ndev; 26695fa0405SHaiyang Zhang 26795fa0405SHaiyang Zhang ndev = dev->net_dev->ndev; 26895fa0405SHaiyang Zhang 26995fa0405SHaiyang Zhang spin_lock_irqsave(&dev->request_lock, flags); 27095fa0405SHaiyang Zhang list_for_each_entry(request, &dev->req_list, list_ent) { 27195fa0405SHaiyang Zhang /* 27295fa0405SHaiyang Zhang * All request/response message contains RequestId as the 1st 27395fa0405SHaiyang Zhang * field 27495fa0405SHaiyang Zhang */ 27595fa0405SHaiyang Zhang if (request->request_msg.msg.init_req.req_id 27695fa0405SHaiyang Zhang == resp->msg.init_complete.req_id) { 27795fa0405SHaiyang Zhang found = true; 27895fa0405SHaiyang Zhang break; 27995fa0405SHaiyang Zhang } 28095fa0405SHaiyang Zhang } 28195fa0405SHaiyang Zhang spin_unlock_irqrestore(&dev->request_lock, flags); 28295fa0405SHaiyang Zhang 28395fa0405SHaiyang Zhang if (found) { 284a3a6cab5SHaiyang Zhang if (resp->msg_len <= 285a3a6cab5SHaiyang Zhang sizeof(struct rndis_message) + RNDIS_EXT_LEN) { 28695fa0405SHaiyang Zhang memcpy(&request->response_msg, resp, 28795fa0405SHaiyang Zhang resp->msg_len); 2881b07da51SHaiyang Zhang if (request->request_msg.ndis_msg_type == 2891b07da51SHaiyang Zhang RNDIS_MSG_QUERY && request->request_msg.msg. 2901b07da51SHaiyang Zhang query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS) 2911b07da51SHaiyang Zhang rndis_set_link_state(dev, request); 29295fa0405SHaiyang Zhang } else { 29395fa0405SHaiyang Zhang netdev_err(ndev, 29495fa0405SHaiyang Zhang "rndis response buffer overflow " 29595fa0405SHaiyang Zhang "detected (size %u max %zu)\n", 29695fa0405SHaiyang Zhang resp->msg_len, 29786eedaccSKY Srinivasan sizeof(struct rndis_message)); 29895fa0405SHaiyang Zhang 29995fa0405SHaiyang Zhang if (resp->ndis_msg_type == 30051491167SLinus Walleij RNDIS_MSG_RESET_C) { 30195fa0405SHaiyang Zhang /* does not have a request id field */ 30295fa0405SHaiyang Zhang request->response_msg.msg.reset_complete. 303007e5c8eSLinus Walleij status = RNDIS_STATUS_BUFFER_OVERFLOW; 30495fa0405SHaiyang Zhang } else { 30595fa0405SHaiyang Zhang request->response_msg.msg. 30695fa0405SHaiyang Zhang init_complete.status = 307007e5c8eSLinus Walleij RNDIS_STATUS_BUFFER_OVERFLOW; 30895fa0405SHaiyang Zhang } 30995fa0405SHaiyang Zhang } 31095fa0405SHaiyang Zhang 31195fa0405SHaiyang Zhang complete(&request->wait_event); 31295fa0405SHaiyang Zhang } else { 31395fa0405SHaiyang Zhang netdev_err(ndev, 31495fa0405SHaiyang Zhang "no rndis request found for this response " 31595fa0405SHaiyang Zhang "(id 0x%x res type 0x%x)\n", 31695fa0405SHaiyang Zhang resp->msg.init_complete.req_id, 31795fa0405SHaiyang Zhang resp->ndis_msg_type); 31895fa0405SHaiyang Zhang } 31995fa0405SHaiyang Zhang } 32095fa0405SHaiyang Zhang 32195fa0405SHaiyang Zhang static void rndis_filter_receive_indicate_status(struct rndis_device *dev, 32295fa0405SHaiyang Zhang struct rndis_message *resp) 32395fa0405SHaiyang Zhang { 32495fa0405SHaiyang Zhang struct rndis_indicate_status *indicate = 32595fa0405SHaiyang Zhang &resp->msg.indicate_status; 32695fa0405SHaiyang Zhang 32795fa0405SHaiyang Zhang if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { 32895fa0405SHaiyang Zhang netvsc_linkstatus_callback( 32995fa0405SHaiyang Zhang dev->net_dev->dev, 1); 33095fa0405SHaiyang Zhang } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { 33195fa0405SHaiyang Zhang netvsc_linkstatus_callback( 33295fa0405SHaiyang Zhang dev->net_dev->dev, 0); 33395fa0405SHaiyang Zhang } else { 33495fa0405SHaiyang Zhang /* 33595fa0405SHaiyang Zhang * TODO: 33695fa0405SHaiyang Zhang */ 33795fa0405SHaiyang Zhang } 33895fa0405SHaiyang Zhang } 33995fa0405SHaiyang Zhang 3401f5f3a75SHaiyang Zhang /* 3411f5f3a75SHaiyang Zhang * Get the Per-Packet-Info with the specified type 3421f5f3a75SHaiyang Zhang * return NULL if not found. 3431f5f3a75SHaiyang Zhang */ 3441f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) 3451f5f3a75SHaiyang Zhang { 3461f5f3a75SHaiyang Zhang struct rndis_per_packet_info *ppi; 3471f5f3a75SHaiyang Zhang int len; 3481f5f3a75SHaiyang Zhang 3491f5f3a75SHaiyang Zhang if (rpkt->per_pkt_info_offset == 0) 3501f5f3a75SHaiyang Zhang return NULL; 3511f5f3a75SHaiyang Zhang 3521f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)rpkt + 3531f5f3a75SHaiyang Zhang rpkt->per_pkt_info_offset); 3541f5f3a75SHaiyang Zhang len = rpkt->per_pkt_info_len; 3551f5f3a75SHaiyang Zhang 3561f5f3a75SHaiyang Zhang while (len > 0) { 3571f5f3a75SHaiyang Zhang if (ppi->type == type) 3581f5f3a75SHaiyang Zhang return (void *)((ulong)ppi + ppi->ppi_offset); 3591f5f3a75SHaiyang Zhang len -= ppi->size; 3601f5f3a75SHaiyang Zhang ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size); 3611f5f3a75SHaiyang Zhang } 3621f5f3a75SHaiyang Zhang 3631f5f3a75SHaiyang Zhang return NULL; 3641f5f3a75SHaiyang Zhang } 3651f5f3a75SHaiyang Zhang 36695fa0405SHaiyang Zhang static void rndis_filter_receive_data(struct rndis_device *dev, 36795fa0405SHaiyang Zhang struct rndis_message *msg, 36895fa0405SHaiyang Zhang struct hv_netvsc_packet *pkt) 36995fa0405SHaiyang Zhang { 37095fa0405SHaiyang Zhang struct rndis_packet *rndis_pkt; 37195fa0405SHaiyang Zhang u32 data_offset; 3721f5f3a75SHaiyang Zhang struct ndis_pkt_8021q_info *vlan; 373e3d605edSKY Srinivasan struct ndis_tcp_ip_checksum_info *csum_info; 37495fa0405SHaiyang Zhang 37595fa0405SHaiyang Zhang rndis_pkt = &msg->msg.pkt; 37695fa0405SHaiyang Zhang 37795fa0405SHaiyang Zhang /* Remove the rndis header and pass it back up the stack */ 37895fa0405SHaiyang Zhang data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; 37995fa0405SHaiyang Zhang 38095fa0405SHaiyang Zhang pkt->total_data_buflen -= data_offset; 3814b8a8bc9SWei Yongjun 3824b8a8bc9SWei Yongjun /* 3834b8a8bc9SWei Yongjun * Make sure we got a valid RNDIS message, now total_data_buflen 3844b8a8bc9SWei Yongjun * should be the data packet size plus the trailer padding size 3854b8a8bc9SWei Yongjun */ 3864b8a8bc9SWei Yongjun if (pkt->total_data_buflen < rndis_pkt->data_len) { 3874b8a8bc9SWei Yongjun netdev_err(dev->net_dev->ndev, "rndis message buffer " 3884b8a8bc9SWei Yongjun "overflow detected (got %u, min %u)" 3894b8a8bc9SWei Yongjun "...dropping this message!\n", 3904b8a8bc9SWei Yongjun pkt->total_data_buflen, rndis_pkt->data_len); 3914b8a8bc9SWei Yongjun return; 3924b8a8bc9SWei Yongjun } 3934b8a8bc9SWei Yongjun 3944b8a8bc9SWei Yongjun /* 3954b8a8bc9SWei Yongjun * Remove the rndis trailer padding from rndis packet message 3964b8a8bc9SWei Yongjun * rndis_pkt->data_len tell us the real data length, we only copy 3974b8a8bc9SWei Yongjun * the data packet to the stack, without the rndis trailer padding 3984b8a8bc9SWei Yongjun */ 3994b8a8bc9SWei Yongjun pkt->total_data_buflen = rndis_pkt->data_len; 40045326342SHaiyang Zhang pkt->data = (void *)((unsigned long)pkt->data + data_offset); 40195fa0405SHaiyang Zhang 40295fa0405SHaiyang Zhang pkt->is_data_pkt = true; 40395fa0405SHaiyang Zhang 4041f5f3a75SHaiyang Zhang vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); 4051f5f3a75SHaiyang Zhang if (vlan) { 4061f5f3a75SHaiyang Zhang pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | 4071f5f3a75SHaiyang Zhang (vlan->pri << VLAN_PRIO_SHIFT); 4081f5f3a75SHaiyang Zhang } else { 4091f5f3a75SHaiyang Zhang pkt->vlan_tci = 0; 4101f5f3a75SHaiyang Zhang } 4111f5f3a75SHaiyang Zhang 412e3d605edSKY Srinivasan csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); 413e3d605edSKY Srinivasan netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info); 41495fa0405SHaiyang Zhang } 41595fa0405SHaiyang Zhang 41695fa0405SHaiyang Zhang int rndis_filter_receive(struct hv_device *dev, 41795fa0405SHaiyang Zhang struct hv_netvsc_packet *pkt) 41895fa0405SHaiyang Zhang { 41995fa0405SHaiyang Zhang struct netvsc_device *net_dev = hv_get_drvdata(dev); 42095fa0405SHaiyang Zhang struct rndis_device *rndis_dev; 421ef31bef6SHaiyang Zhang struct rndis_message *rndis_msg; 42295fa0405SHaiyang Zhang struct net_device *ndev; 42363f6921dSHaiyang Zhang int ret = 0; 42495fa0405SHaiyang Zhang 42563f6921dSHaiyang Zhang if (!net_dev) { 42663f6921dSHaiyang Zhang ret = -EINVAL; 42763f6921dSHaiyang Zhang goto exit; 42863f6921dSHaiyang Zhang } 42995fa0405SHaiyang Zhang 43095fa0405SHaiyang Zhang ndev = net_dev->ndev; 43195fa0405SHaiyang Zhang 43295fa0405SHaiyang Zhang /* Make sure the rndis device state is initialized */ 43395fa0405SHaiyang Zhang if (!net_dev->extension) { 43495fa0405SHaiyang Zhang netdev_err(ndev, "got rndis message but no rndis device - " 43595fa0405SHaiyang Zhang "dropping this message!\n"); 43663f6921dSHaiyang Zhang ret = -ENODEV; 43763f6921dSHaiyang Zhang goto exit; 43895fa0405SHaiyang Zhang } 43995fa0405SHaiyang Zhang 44095fa0405SHaiyang Zhang rndis_dev = (struct rndis_device *)net_dev->extension; 44195fa0405SHaiyang Zhang if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { 44295fa0405SHaiyang Zhang netdev_err(ndev, "got rndis message but rndis device " 44395fa0405SHaiyang Zhang "uninitialized...dropping this message!\n"); 44463f6921dSHaiyang Zhang ret = -ENODEV; 44563f6921dSHaiyang Zhang goto exit; 44695fa0405SHaiyang Zhang } 44795fa0405SHaiyang Zhang 448ef31bef6SHaiyang Zhang rndis_msg = pkt->data; 44995fa0405SHaiyang Zhang 450ef31bef6SHaiyang Zhang dump_rndis_message(dev, rndis_msg); 45195fa0405SHaiyang Zhang 452ef31bef6SHaiyang Zhang switch (rndis_msg->ndis_msg_type) { 45351491167SLinus Walleij case RNDIS_MSG_PACKET: 45495fa0405SHaiyang Zhang /* data msg */ 455ef31bef6SHaiyang Zhang rndis_filter_receive_data(rndis_dev, rndis_msg, pkt); 45695fa0405SHaiyang Zhang break; 45795fa0405SHaiyang Zhang 45851491167SLinus Walleij case RNDIS_MSG_INIT_C: 45951491167SLinus Walleij case RNDIS_MSG_QUERY_C: 46051491167SLinus Walleij case RNDIS_MSG_SET_C: 46195fa0405SHaiyang Zhang /* completion msgs */ 462ef31bef6SHaiyang Zhang rndis_filter_receive_response(rndis_dev, rndis_msg); 46395fa0405SHaiyang Zhang break; 46495fa0405SHaiyang Zhang 46551491167SLinus Walleij case RNDIS_MSG_INDICATE: 46695fa0405SHaiyang Zhang /* notification msgs */ 467ef31bef6SHaiyang Zhang rndis_filter_receive_indicate_status(rndis_dev, rndis_msg); 46895fa0405SHaiyang Zhang break; 46995fa0405SHaiyang Zhang default: 47095fa0405SHaiyang Zhang netdev_err(ndev, 47195fa0405SHaiyang Zhang "unhandled rndis message (type %u len %u)\n", 472ef31bef6SHaiyang Zhang rndis_msg->ndis_msg_type, 473ef31bef6SHaiyang Zhang rndis_msg->msg_len); 47495fa0405SHaiyang Zhang break; 47595fa0405SHaiyang Zhang } 47695fa0405SHaiyang Zhang 47763f6921dSHaiyang Zhang exit: 47863f6921dSHaiyang Zhang if (ret != 0) 47963f6921dSHaiyang Zhang pkt->status = NVSP_STAT_FAIL; 48063f6921dSHaiyang Zhang 48163f6921dSHaiyang Zhang return ret; 48295fa0405SHaiyang Zhang } 48395fa0405SHaiyang Zhang 48495fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, 48595fa0405SHaiyang Zhang void *result, u32 *result_size) 48695fa0405SHaiyang Zhang { 48795fa0405SHaiyang Zhang struct rndis_request *request; 48895fa0405SHaiyang Zhang u32 inresult_size = *result_size; 48995fa0405SHaiyang Zhang struct rndis_query_request *query; 49095fa0405SHaiyang Zhang struct rndis_query_complete *query_complete; 49195fa0405SHaiyang Zhang int ret = 0; 49295fa0405SHaiyang Zhang int t; 49395fa0405SHaiyang Zhang 49495fa0405SHaiyang Zhang if (!result) 49595fa0405SHaiyang Zhang return -EINVAL; 49695fa0405SHaiyang Zhang 49795fa0405SHaiyang Zhang *result_size = 0; 49851491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_QUERY, 49995fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_query_request)); 50095fa0405SHaiyang Zhang if (!request) { 50195fa0405SHaiyang Zhang ret = -ENOMEM; 50295fa0405SHaiyang Zhang goto cleanup; 50395fa0405SHaiyang Zhang } 50495fa0405SHaiyang Zhang 50595fa0405SHaiyang Zhang /* Setup the rndis query */ 50695fa0405SHaiyang Zhang query = &request->request_msg.msg.query_req; 50795fa0405SHaiyang Zhang query->oid = oid; 50895fa0405SHaiyang Zhang query->info_buf_offset = sizeof(struct rndis_query_request); 50995fa0405SHaiyang Zhang query->info_buflen = 0; 51095fa0405SHaiyang Zhang query->dev_vc_handle = 0; 51195fa0405SHaiyang Zhang 51295fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 51395fa0405SHaiyang Zhang if (ret != 0) 51495fa0405SHaiyang Zhang goto cleanup; 51595fa0405SHaiyang Zhang 51695fa0405SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 51795fa0405SHaiyang Zhang if (t == 0) { 51895fa0405SHaiyang Zhang ret = -ETIMEDOUT; 51995fa0405SHaiyang Zhang goto cleanup; 52095fa0405SHaiyang Zhang } 52195fa0405SHaiyang Zhang 52295fa0405SHaiyang Zhang /* Copy the response back */ 52395fa0405SHaiyang Zhang query_complete = &request->response_msg.msg.query_complete; 52495fa0405SHaiyang Zhang 52595fa0405SHaiyang Zhang if (query_complete->info_buflen > inresult_size) { 52695fa0405SHaiyang Zhang ret = -1; 52795fa0405SHaiyang Zhang goto cleanup; 52895fa0405SHaiyang Zhang } 52995fa0405SHaiyang Zhang 53095fa0405SHaiyang Zhang memcpy(result, 53195fa0405SHaiyang Zhang (void *)((unsigned long)query_complete + 53295fa0405SHaiyang Zhang query_complete->info_buf_offset), 53395fa0405SHaiyang Zhang query_complete->info_buflen); 53495fa0405SHaiyang Zhang 53595fa0405SHaiyang Zhang *result_size = query_complete->info_buflen; 53695fa0405SHaiyang Zhang 53795fa0405SHaiyang Zhang cleanup: 53895fa0405SHaiyang Zhang if (request) 53995fa0405SHaiyang Zhang put_rndis_request(dev, request); 54095fa0405SHaiyang Zhang 54195fa0405SHaiyang Zhang return ret; 54295fa0405SHaiyang Zhang } 54395fa0405SHaiyang Zhang 54495fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev) 54595fa0405SHaiyang Zhang { 54695fa0405SHaiyang Zhang u32 size = ETH_ALEN; 54795fa0405SHaiyang Zhang 54895fa0405SHaiyang Zhang return rndis_filter_query_device(dev, 54995fa0405SHaiyang Zhang RNDIS_OID_802_3_PERMANENT_ADDRESS, 55095fa0405SHaiyang Zhang dev->hw_mac_adr, &size); 55195fa0405SHaiyang Zhang } 55295fa0405SHaiyang Zhang 5531ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress" 5541ce09e89SHaiyang Zhang #define NWADR_STRLEN 14 5551ce09e89SHaiyang Zhang 5561ce09e89SHaiyang Zhang int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) 5571ce09e89SHaiyang Zhang { 5581ce09e89SHaiyang Zhang struct netvsc_device *nvdev = hv_get_drvdata(hdev); 5591ce09e89SHaiyang Zhang struct rndis_device *rdev = nvdev->extension; 5601ce09e89SHaiyang Zhang struct net_device *ndev = nvdev->ndev; 5611ce09e89SHaiyang Zhang struct rndis_request *request; 5621ce09e89SHaiyang Zhang struct rndis_set_request *set; 5631ce09e89SHaiyang Zhang struct rndis_config_parameter_info *cpi; 5641ce09e89SHaiyang Zhang wchar_t *cfg_nwadr, *cfg_mac; 5651ce09e89SHaiyang Zhang struct rndis_set_complete *set_complete; 5661ce09e89SHaiyang Zhang char macstr[2*ETH_ALEN+1]; 5671ce09e89SHaiyang Zhang u32 extlen = sizeof(struct rndis_config_parameter_info) + 5681ce09e89SHaiyang Zhang 2*NWADR_STRLEN + 4*ETH_ALEN; 5691ce09e89SHaiyang Zhang int ret, t; 5701ce09e89SHaiyang Zhang 5711ce09e89SHaiyang Zhang request = get_rndis_request(rdev, RNDIS_MSG_SET, 5721ce09e89SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 5731ce09e89SHaiyang Zhang if (!request) 5741ce09e89SHaiyang Zhang return -ENOMEM; 5751ce09e89SHaiyang Zhang 5761ce09e89SHaiyang Zhang set = &request->request_msg.msg.set_req; 5771ce09e89SHaiyang Zhang set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER; 5781ce09e89SHaiyang Zhang set->info_buflen = extlen; 5791ce09e89SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 5801ce09e89SHaiyang Zhang set->dev_vc_handle = 0; 5811ce09e89SHaiyang Zhang 5821ce09e89SHaiyang Zhang cpi = (struct rndis_config_parameter_info *)((ulong)set + 5831ce09e89SHaiyang Zhang set->info_buf_offset); 5841ce09e89SHaiyang Zhang cpi->parameter_name_offset = 5851ce09e89SHaiyang Zhang sizeof(struct rndis_config_parameter_info); 5861ce09e89SHaiyang Zhang /* Multiply by 2 because host needs 2 bytes (utf16) for each char */ 5871ce09e89SHaiyang Zhang cpi->parameter_name_length = 2*NWADR_STRLEN; 5881ce09e89SHaiyang Zhang cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING; 5891ce09e89SHaiyang Zhang cpi->parameter_value_offset = 5901ce09e89SHaiyang Zhang cpi->parameter_name_offset + cpi->parameter_name_length; 5911ce09e89SHaiyang Zhang /* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */ 5921ce09e89SHaiyang Zhang cpi->parameter_value_length = 4*ETH_ALEN; 5931ce09e89SHaiyang Zhang 5941ce09e89SHaiyang Zhang cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset); 5951ce09e89SHaiyang Zhang cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset); 5961ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN, 5971ce09e89SHaiyang Zhang cfg_nwadr, NWADR_STRLEN); 5981ce09e89SHaiyang Zhang if (ret < 0) 5991ce09e89SHaiyang Zhang goto cleanup; 6001ce09e89SHaiyang Zhang snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac); 6011ce09e89SHaiyang Zhang ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN, 6021ce09e89SHaiyang Zhang cfg_mac, 2*ETH_ALEN); 6031ce09e89SHaiyang Zhang if (ret < 0) 6041ce09e89SHaiyang Zhang goto cleanup; 6051ce09e89SHaiyang Zhang 6061ce09e89SHaiyang Zhang ret = rndis_filter_send_request(rdev, request); 6071ce09e89SHaiyang Zhang if (ret != 0) 6081ce09e89SHaiyang Zhang goto cleanup; 6091ce09e89SHaiyang Zhang 6101ce09e89SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 6111ce09e89SHaiyang Zhang if (t == 0) { 6121ce09e89SHaiyang Zhang netdev_err(ndev, "timeout before we got a set response...\n"); 6131ce09e89SHaiyang Zhang /* 6141ce09e89SHaiyang Zhang * can't put_rndis_request, since we may still receive a 6151ce09e89SHaiyang Zhang * send-completion. 6161ce09e89SHaiyang Zhang */ 6171ce09e89SHaiyang Zhang return -EBUSY; 6181ce09e89SHaiyang Zhang } else { 6191ce09e89SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 620b02a8067SHaiyang Zhang if (set_complete->status != RNDIS_STATUS_SUCCESS) { 621b02a8067SHaiyang Zhang netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", 622b02a8067SHaiyang Zhang set_complete->status); 6231ce09e89SHaiyang Zhang ret = -EINVAL; 6241ce09e89SHaiyang Zhang } 625b02a8067SHaiyang Zhang } 6261ce09e89SHaiyang Zhang 6271ce09e89SHaiyang Zhang cleanup: 6281ce09e89SHaiyang Zhang put_rndis_request(rdev, request); 6291ce09e89SHaiyang Zhang return ret; 6301ce09e89SHaiyang Zhang } 6311ce09e89SHaiyang Zhang 6324a0e70aeSKY Srinivasan int rndis_filter_set_offload_params(struct hv_device *hdev, 6334a0e70aeSKY Srinivasan struct ndis_offload_params *req_offloads) 6344a0e70aeSKY Srinivasan { 6354a0e70aeSKY Srinivasan struct netvsc_device *nvdev = hv_get_drvdata(hdev); 6364a0e70aeSKY Srinivasan struct rndis_device *rdev = nvdev->extension; 6374a0e70aeSKY Srinivasan struct net_device *ndev = nvdev->ndev; 6384a0e70aeSKY Srinivasan struct rndis_request *request; 6394a0e70aeSKY Srinivasan struct rndis_set_request *set; 6404a0e70aeSKY Srinivasan struct ndis_offload_params *offload_params; 6414a0e70aeSKY Srinivasan struct rndis_set_complete *set_complete; 6424a0e70aeSKY Srinivasan u32 extlen = sizeof(struct ndis_offload_params); 6434a0e70aeSKY Srinivasan int ret, t; 644af9893a3SKY Srinivasan u32 vsp_version = nvdev->nvsp_version; 645af9893a3SKY Srinivasan 646af9893a3SKY Srinivasan if (vsp_version <= NVSP_PROTOCOL_VERSION_4) { 647af9893a3SKY Srinivasan extlen = VERSION_4_OFFLOAD_SIZE; 648af9893a3SKY Srinivasan /* On NVSP_PROTOCOL_VERSION_4 and below, we do not support 649af9893a3SKY Srinivasan * UDP checksum offload. 650af9893a3SKY Srinivasan */ 651af9893a3SKY Srinivasan req_offloads->udp_ip_v4_csum = 0; 652af9893a3SKY Srinivasan req_offloads->udp_ip_v6_csum = 0; 653af9893a3SKY Srinivasan } 6544a0e70aeSKY Srinivasan 6554a0e70aeSKY Srinivasan request = get_rndis_request(rdev, RNDIS_MSG_SET, 6564a0e70aeSKY Srinivasan RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); 6574a0e70aeSKY Srinivasan if (!request) 6584a0e70aeSKY Srinivasan return -ENOMEM; 6594a0e70aeSKY Srinivasan 6604a0e70aeSKY Srinivasan set = &request->request_msg.msg.set_req; 6614a0e70aeSKY Srinivasan set->oid = OID_TCP_OFFLOAD_PARAMETERS; 6624a0e70aeSKY Srinivasan set->info_buflen = extlen; 6634a0e70aeSKY Srinivasan set->info_buf_offset = sizeof(struct rndis_set_request); 6644a0e70aeSKY Srinivasan set->dev_vc_handle = 0; 6654a0e70aeSKY Srinivasan 6664a0e70aeSKY Srinivasan offload_params = (struct ndis_offload_params *)((ulong)set + 6674a0e70aeSKY Srinivasan set->info_buf_offset); 6684a0e70aeSKY Srinivasan *offload_params = *req_offloads; 6694a0e70aeSKY Srinivasan offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT; 6704a0e70aeSKY Srinivasan offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3; 6714a0e70aeSKY Srinivasan offload_params->header.size = extlen; 6724a0e70aeSKY Srinivasan 6734a0e70aeSKY Srinivasan ret = rndis_filter_send_request(rdev, request); 6744a0e70aeSKY Srinivasan if (ret != 0) 6754a0e70aeSKY Srinivasan goto cleanup; 6764a0e70aeSKY Srinivasan 6774a0e70aeSKY Srinivasan t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 6784a0e70aeSKY Srinivasan if (t == 0) { 6794a0e70aeSKY Srinivasan netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n"); 6804a0e70aeSKY Srinivasan /* can't put_rndis_request, since we may still receive a 6814a0e70aeSKY Srinivasan * send-completion. 6824a0e70aeSKY Srinivasan */ 6834a0e70aeSKY Srinivasan return -EBUSY; 6844a0e70aeSKY Srinivasan } else { 6854a0e70aeSKY Srinivasan set_complete = &request->response_msg.msg.set_complete; 6864a0e70aeSKY Srinivasan if (set_complete->status != RNDIS_STATUS_SUCCESS) { 687af9893a3SKY Srinivasan netdev_err(ndev, "Fail to set offload on host side:0x%x\n", 6884a0e70aeSKY Srinivasan set_complete->status); 6894a0e70aeSKY Srinivasan ret = -EINVAL; 6904a0e70aeSKY Srinivasan } 6914a0e70aeSKY Srinivasan } 6924a0e70aeSKY Srinivasan 6934a0e70aeSKY Srinivasan cleanup: 6944a0e70aeSKY Srinivasan put_rndis_request(rdev, request); 6954a0e70aeSKY Srinivasan return ret; 6964a0e70aeSKY Srinivasan } 6971ce09e89SHaiyang Zhang 69895fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev) 69995fa0405SHaiyang Zhang { 70095fa0405SHaiyang Zhang u32 size = sizeof(u32); 70195fa0405SHaiyang Zhang u32 link_status; 70295fa0405SHaiyang Zhang int ret; 70395fa0405SHaiyang Zhang 70495fa0405SHaiyang Zhang ret = rndis_filter_query_device(dev, 70595fa0405SHaiyang Zhang RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, 70695fa0405SHaiyang Zhang &link_status, &size); 70795fa0405SHaiyang Zhang 70895fa0405SHaiyang Zhang return ret; 70995fa0405SHaiyang Zhang } 71095fa0405SHaiyang Zhang 711d426b2e3SHaiyang Zhang int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) 71295fa0405SHaiyang Zhang { 71395fa0405SHaiyang Zhang struct rndis_request *request; 71495fa0405SHaiyang Zhang struct rndis_set_request *set; 71595fa0405SHaiyang Zhang struct rndis_set_complete *set_complete; 71695fa0405SHaiyang Zhang u32 status; 71795fa0405SHaiyang Zhang int ret, t; 71895fa0405SHaiyang Zhang struct net_device *ndev; 71995fa0405SHaiyang Zhang 72095fa0405SHaiyang Zhang ndev = dev->net_dev->ndev; 72195fa0405SHaiyang Zhang 72251491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_SET, 72395fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_set_request) + 72495fa0405SHaiyang Zhang sizeof(u32)); 72595fa0405SHaiyang Zhang if (!request) { 72695fa0405SHaiyang Zhang ret = -ENOMEM; 72795fa0405SHaiyang Zhang goto cleanup; 72895fa0405SHaiyang Zhang } 72995fa0405SHaiyang Zhang 73095fa0405SHaiyang Zhang /* Setup the rndis set */ 73195fa0405SHaiyang Zhang set = &request->request_msg.msg.set_req; 73295fa0405SHaiyang Zhang set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; 73395fa0405SHaiyang Zhang set->info_buflen = sizeof(u32); 73495fa0405SHaiyang Zhang set->info_buf_offset = sizeof(struct rndis_set_request); 73595fa0405SHaiyang Zhang 73695fa0405SHaiyang Zhang memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), 73795fa0405SHaiyang Zhang &new_filter, sizeof(u32)); 73895fa0405SHaiyang Zhang 73995fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 74095fa0405SHaiyang Zhang if (ret != 0) 74195fa0405SHaiyang Zhang goto cleanup; 74295fa0405SHaiyang Zhang 74395fa0405SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 74495fa0405SHaiyang Zhang 74595fa0405SHaiyang Zhang if (t == 0) { 74695fa0405SHaiyang Zhang netdev_err(ndev, 74795fa0405SHaiyang Zhang "timeout before we got a set response...\n"); 748ea496374SHaiyang Zhang ret = -ETIMEDOUT; 74995fa0405SHaiyang Zhang /* 75095fa0405SHaiyang Zhang * We can't deallocate the request since we may still receive a 75195fa0405SHaiyang Zhang * send completion for it. 75295fa0405SHaiyang Zhang */ 75395fa0405SHaiyang Zhang goto exit; 75495fa0405SHaiyang Zhang } else { 75595fa0405SHaiyang Zhang set_complete = &request->response_msg.msg.set_complete; 75695fa0405SHaiyang Zhang status = set_complete->status; 75795fa0405SHaiyang Zhang } 75895fa0405SHaiyang Zhang 75995fa0405SHaiyang Zhang cleanup: 76095fa0405SHaiyang Zhang if (request) 76195fa0405SHaiyang Zhang put_rndis_request(dev, request); 76295fa0405SHaiyang Zhang exit: 76395fa0405SHaiyang Zhang return ret; 76495fa0405SHaiyang Zhang } 76595fa0405SHaiyang Zhang 76695fa0405SHaiyang Zhang 76795fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev) 76895fa0405SHaiyang Zhang { 76995fa0405SHaiyang Zhang struct rndis_request *request; 77095fa0405SHaiyang Zhang struct rndis_initialize_request *init; 77195fa0405SHaiyang Zhang struct rndis_initialize_complete *init_complete; 77295fa0405SHaiyang Zhang u32 status; 77395fa0405SHaiyang Zhang int ret, t; 77495fa0405SHaiyang Zhang 77551491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_INIT, 77695fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); 77795fa0405SHaiyang Zhang if (!request) { 77895fa0405SHaiyang Zhang ret = -ENOMEM; 77995fa0405SHaiyang Zhang goto cleanup; 78095fa0405SHaiyang Zhang } 78195fa0405SHaiyang Zhang 78295fa0405SHaiyang Zhang /* Setup the rndis set */ 78395fa0405SHaiyang Zhang init = &request->request_msg.msg.init_req; 78495fa0405SHaiyang Zhang init->major_ver = RNDIS_MAJOR_VERSION; 78595fa0405SHaiyang Zhang init->minor_ver = RNDIS_MINOR_VERSION; 786fb1d074eSHaiyang Zhang init->max_xfer_size = 0x4000; 78795fa0405SHaiyang Zhang 78895fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZING; 78995fa0405SHaiyang Zhang 79095fa0405SHaiyang Zhang ret = rndis_filter_send_request(dev, request); 79195fa0405SHaiyang Zhang if (ret != 0) { 79295fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 79395fa0405SHaiyang Zhang goto cleanup; 79495fa0405SHaiyang Zhang } 79595fa0405SHaiyang Zhang 79695fa0405SHaiyang Zhang 79795fa0405SHaiyang Zhang t = wait_for_completion_timeout(&request->wait_event, 5*HZ); 79895fa0405SHaiyang Zhang 79995fa0405SHaiyang Zhang if (t == 0) { 80095fa0405SHaiyang Zhang ret = -ETIMEDOUT; 80195fa0405SHaiyang Zhang goto cleanup; 80295fa0405SHaiyang Zhang } 80395fa0405SHaiyang Zhang 80495fa0405SHaiyang Zhang init_complete = &request->response_msg.msg.init_complete; 80595fa0405SHaiyang Zhang status = init_complete->status; 80695fa0405SHaiyang Zhang if (status == RNDIS_STATUS_SUCCESS) { 80795fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 80895fa0405SHaiyang Zhang ret = 0; 80995fa0405SHaiyang Zhang } else { 81095fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 81195fa0405SHaiyang Zhang ret = -EINVAL; 81295fa0405SHaiyang Zhang } 81395fa0405SHaiyang Zhang 81495fa0405SHaiyang Zhang cleanup: 81595fa0405SHaiyang Zhang if (request) 81695fa0405SHaiyang Zhang put_rndis_request(dev, request); 81795fa0405SHaiyang Zhang 81895fa0405SHaiyang Zhang return ret; 81995fa0405SHaiyang Zhang } 82095fa0405SHaiyang Zhang 82195fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev) 82295fa0405SHaiyang Zhang { 82395fa0405SHaiyang Zhang struct rndis_request *request; 82495fa0405SHaiyang Zhang struct rndis_halt_request *halt; 825ae9e63bbSHaiyang Zhang struct netvsc_device *nvdev = dev->net_dev; 826ae9e63bbSHaiyang Zhang struct hv_device *hdev = nvdev->dev; 827ae9e63bbSHaiyang Zhang ulong flags; 82895fa0405SHaiyang Zhang 82995fa0405SHaiyang Zhang /* Attempt to do a rndis device halt */ 83051491167SLinus Walleij request = get_rndis_request(dev, RNDIS_MSG_HALT, 83195fa0405SHaiyang Zhang RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); 83295fa0405SHaiyang Zhang if (!request) 83395fa0405SHaiyang Zhang goto cleanup; 83495fa0405SHaiyang Zhang 83595fa0405SHaiyang Zhang /* Setup the rndis set */ 83695fa0405SHaiyang Zhang halt = &request->request_msg.msg.halt_req; 83795fa0405SHaiyang Zhang halt->req_id = atomic_inc_return(&dev->new_req_id); 83895fa0405SHaiyang Zhang 83995fa0405SHaiyang Zhang /* Ignore return since this msg is optional. */ 84095fa0405SHaiyang Zhang rndis_filter_send_request(dev, request); 84195fa0405SHaiyang Zhang 84295fa0405SHaiyang Zhang dev->state = RNDIS_DEV_UNINITIALIZED; 84395fa0405SHaiyang Zhang 84495fa0405SHaiyang Zhang cleanup: 845ae9e63bbSHaiyang Zhang spin_lock_irqsave(&hdev->channel->inbound_lock, flags); 846ae9e63bbSHaiyang Zhang nvdev->destroy = true; 847ae9e63bbSHaiyang Zhang spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags); 848ae9e63bbSHaiyang Zhang 849ae9e63bbSHaiyang Zhang /* Wait for all send completions */ 850ae9e63bbSHaiyang Zhang wait_event(nvdev->wait_drain, 851ae9e63bbSHaiyang Zhang atomic_read(&nvdev->num_outstanding_sends) == 0); 852ae9e63bbSHaiyang Zhang 85395fa0405SHaiyang Zhang if (request) 85495fa0405SHaiyang Zhang put_rndis_request(dev, request); 85595fa0405SHaiyang Zhang return; 85695fa0405SHaiyang Zhang } 85795fa0405SHaiyang Zhang 85895fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev) 85995fa0405SHaiyang Zhang { 86095fa0405SHaiyang Zhang int ret; 86195fa0405SHaiyang Zhang 86295fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_INITIALIZED) 86395fa0405SHaiyang Zhang return 0; 86495fa0405SHaiyang Zhang 86595fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 86695fa0405SHaiyang Zhang NDIS_PACKET_TYPE_BROADCAST | 86795fa0405SHaiyang Zhang NDIS_PACKET_TYPE_ALL_MULTICAST | 86895fa0405SHaiyang Zhang NDIS_PACKET_TYPE_DIRECTED); 86995fa0405SHaiyang Zhang if (ret == 0) 87095fa0405SHaiyang Zhang dev->state = RNDIS_DEV_DATAINITIALIZED; 87195fa0405SHaiyang Zhang 87295fa0405SHaiyang Zhang return ret; 87395fa0405SHaiyang Zhang } 87495fa0405SHaiyang Zhang 87595fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev) 87695fa0405SHaiyang Zhang { 87795fa0405SHaiyang Zhang int ret; 87895fa0405SHaiyang Zhang 87995fa0405SHaiyang Zhang if (dev->state != RNDIS_DEV_DATAINITIALIZED) 88095fa0405SHaiyang Zhang return 0; 88195fa0405SHaiyang Zhang 88295fa0405SHaiyang Zhang ret = rndis_filter_set_packet_filter(dev, 0); 88395fa0405SHaiyang Zhang if (ret == 0) 88495fa0405SHaiyang Zhang dev->state = RNDIS_DEV_INITIALIZED; 88595fa0405SHaiyang Zhang 88695fa0405SHaiyang Zhang return ret; 88795fa0405SHaiyang Zhang } 88895fa0405SHaiyang Zhang 88995fa0405SHaiyang Zhang int rndis_filter_device_add(struct hv_device *dev, 89095fa0405SHaiyang Zhang void *additional_info) 89195fa0405SHaiyang Zhang { 89295fa0405SHaiyang Zhang int ret; 89395fa0405SHaiyang Zhang struct netvsc_device *net_device; 89495fa0405SHaiyang Zhang struct rndis_device *rndis_device; 89595fa0405SHaiyang Zhang struct netvsc_device_info *device_info = additional_info; 8964a0e70aeSKY Srinivasan struct ndis_offload_params offloads; 89795fa0405SHaiyang Zhang 89895fa0405SHaiyang Zhang rndis_device = get_rndis_device(); 89995fa0405SHaiyang Zhang if (!rndis_device) 90095fa0405SHaiyang Zhang return -ENODEV; 90195fa0405SHaiyang Zhang 90295fa0405SHaiyang Zhang /* 90395fa0405SHaiyang Zhang * Let the inner driver handle this first to create the netvsc channel 90495fa0405SHaiyang Zhang * NOTE! Once the channel is created, we may get a receive callback 90595fa0405SHaiyang Zhang * (RndisFilterOnReceive()) before this call is completed 90695fa0405SHaiyang Zhang */ 90795fa0405SHaiyang Zhang ret = netvsc_device_add(dev, additional_info); 90895fa0405SHaiyang Zhang if (ret != 0) { 90995fa0405SHaiyang Zhang kfree(rndis_device); 91095fa0405SHaiyang Zhang return ret; 91195fa0405SHaiyang Zhang } 91295fa0405SHaiyang Zhang 91395fa0405SHaiyang Zhang 91495fa0405SHaiyang Zhang /* Initialize the rndis device */ 91595fa0405SHaiyang Zhang net_device = hv_get_drvdata(dev); 91695fa0405SHaiyang Zhang 91795fa0405SHaiyang Zhang net_device->extension = rndis_device; 91895fa0405SHaiyang Zhang rndis_device->net_dev = net_device; 91995fa0405SHaiyang Zhang 92095fa0405SHaiyang Zhang /* Send the rndis initialization message */ 92195fa0405SHaiyang Zhang ret = rndis_filter_init_device(rndis_device); 92295fa0405SHaiyang Zhang if (ret != 0) { 9235243e7bdSHaiyang Zhang rndis_filter_device_remove(dev); 9245243e7bdSHaiyang Zhang return ret; 92595fa0405SHaiyang Zhang } 92695fa0405SHaiyang Zhang 92795fa0405SHaiyang Zhang /* Get the mac address */ 92895fa0405SHaiyang Zhang ret = rndis_filter_query_device_mac(rndis_device); 92995fa0405SHaiyang Zhang if (ret != 0) { 9305243e7bdSHaiyang Zhang rndis_filter_device_remove(dev); 9315243e7bdSHaiyang Zhang return ret; 93295fa0405SHaiyang Zhang } 93395fa0405SHaiyang Zhang 93495fa0405SHaiyang Zhang memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); 93595fa0405SHaiyang Zhang 9364a0e70aeSKY Srinivasan /* Turn on the offloads; the host supports all of the relevant 9374a0e70aeSKY Srinivasan * offloads. 9384a0e70aeSKY Srinivasan */ 9394a0e70aeSKY Srinivasan memset(&offloads, 0, sizeof(struct ndis_offload_params)); 9404a0e70aeSKY Srinivasan /* A value of zero means "no change"; now turn on what we 9414a0e70aeSKY Srinivasan * want. 9424a0e70aeSKY Srinivasan */ 9434a0e70aeSKY Srinivasan offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 9444a0e70aeSKY Srinivasan offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 9454a0e70aeSKY Srinivasan offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 9464a0e70aeSKY Srinivasan offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 9474a0e70aeSKY Srinivasan offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; 9484a0e70aeSKY Srinivasan offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; 9494a0e70aeSKY Srinivasan 9504a0e70aeSKY Srinivasan 9514a0e70aeSKY Srinivasan ret = rndis_filter_set_offload_params(dev, &offloads); 9524a0e70aeSKY Srinivasan if (ret) 9534a0e70aeSKY Srinivasan goto err_dev_remv; 9544a0e70aeSKY Srinivasan 9554a0e70aeSKY Srinivasan 95695fa0405SHaiyang Zhang rndis_filter_query_device_link_status(rndis_device); 95795fa0405SHaiyang Zhang 95895fa0405SHaiyang Zhang device_info->link_state = rndis_device->link_state; 95995fa0405SHaiyang Zhang 96095fa0405SHaiyang Zhang dev_info(&dev->device, "Device MAC %pM link state %s\n", 96195fa0405SHaiyang Zhang rndis_device->hw_mac_adr, 96295fa0405SHaiyang Zhang device_info->link_state ? "down" : "up"); 96395fa0405SHaiyang Zhang 96495fa0405SHaiyang Zhang return ret; 9654a0e70aeSKY Srinivasan 9664a0e70aeSKY Srinivasan err_dev_remv: 9674a0e70aeSKY Srinivasan rndis_filter_device_remove(dev); 9684a0e70aeSKY Srinivasan return ret; 96995fa0405SHaiyang Zhang } 97095fa0405SHaiyang Zhang 97195fa0405SHaiyang Zhang void rndis_filter_device_remove(struct hv_device *dev) 97295fa0405SHaiyang Zhang { 97395fa0405SHaiyang Zhang struct netvsc_device *net_dev = hv_get_drvdata(dev); 97495fa0405SHaiyang Zhang struct rndis_device *rndis_dev = net_dev->extension; 97595fa0405SHaiyang Zhang 97695fa0405SHaiyang Zhang /* Halt and release the rndis device */ 97795fa0405SHaiyang Zhang rndis_filter_halt_device(rndis_dev); 97895fa0405SHaiyang Zhang 97995fa0405SHaiyang Zhang kfree(rndis_dev); 98095fa0405SHaiyang Zhang net_dev->extension = NULL; 98195fa0405SHaiyang Zhang 98295fa0405SHaiyang Zhang netvsc_device_remove(dev); 98395fa0405SHaiyang Zhang } 98495fa0405SHaiyang Zhang 98595fa0405SHaiyang Zhang 98695fa0405SHaiyang Zhang int rndis_filter_open(struct hv_device *dev) 98795fa0405SHaiyang Zhang { 98895fa0405SHaiyang Zhang struct netvsc_device *net_device = hv_get_drvdata(dev); 98995fa0405SHaiyang Zhang 99095fa0405SHaiyang Zhang if (!net_device) 99195fa0405SHaiyang Zhang return -EINVAL; 99295fa0405SHaiyang Zhang 99395fa0405SHaiyang Zhang return rndis_filter_open_device(net_device->extension); 99495fa0405SHaiyang Zhang } 99595fa0405SHaiyang Zhang 99695fa0405SHaiyang Zhang int rndis_filter_close(struct hv_device *dev) 99795fa0405SHaiyang Zhang { 9985fccab3bSHaiyang Zhang struct netvsc_device *nvdev = hv_get_drvdata(dev); 99995fa0405SHaiyang Zhang 10005fccab3bSHaiyang Zhang if (!nvdev) 100195fa0405SHaiyang Zhang return -EINVAL; 100295fa0405SHaiyang Zhang 10035fccab3bSHaiyang Zhang return rndis_filter_close_device(nvdev->extension); 100495fa0405SHaiyang Zhang } 1005