6790 lines
190 KiB
Diff
6790 lines
190 KiB
Diff
From: Haiyang Zhang <haiyangz@microsoft.com>
|
|
Date: Mon, 28 Nov 2011 13:35:35 -0800
|
|
Subject: [PATCH 18/77] staging: hv: move hv_netvsc out of staging area
|
|
|
|
commit 95fa0405c5991726e06c08ffcd8ff872f7fb4f2d upstream.
|
|
|
|
hv_netvsc has been reviewed on netdev mailing list on 6/09/2011.
|
|
All recommended changes have been made. We are requesting to move
|
|
it out of staging area.
|
|
|
|
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
|
|
Signed-off-by: KY Srinivasan <kys@microsoft.com>
|
|
Signed-off-by: Mike Sterling <Mike.Sterling@microsoft.com>
|
|
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
|
|
Acked-by: David S. Miller <davem@davemloft.net>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
|
|
[bwh: Adjust context to apply after commit
|
|
045b1684bc21575793a099490938d197555eb128 ('Staging: hv: mousevsc: Remove the
|
|
mouse driver from the staging tree') which makes adjacent deletions.]
|
|
---
|
|
drivers/net/Kconfig | 2 +
|
|
drivers/net/Makefile | 2 +
|
|
drivers/net/hyperv/Kconfig | 5 +
|
|
drivers/net/hyperv/Makefile | 3 +
|
|
drivers/net/hyperv/hyperv_net.h | 1058 +++++++++++++++++++++++++++++++++++++
|
|
drivers/net/hyperv/netvsc.c | 939 ++++++++++++++++++++++++++++++++
|
|
drivers/net/hyperv/netvsc_drv.c | 456 ++++++++++++++++
|
|
drivers/net/hyperv/rndis_filter.c | 855 ++++++++++++++++++++++++++++++
|
|
drivers/staging/hv/Kconfig | 6 -
|
|
drivers/staging/hv/Makefile | 2 -
|
|
drivers/staging/hv/TODO | 1 -
|
|
drivers/staging/hv/hyperv_net.h | 1058 -------------------------------------
|
|
drivers/staging/hv/netvsc.c | 939 --------------------------------
|
|
drivers/staging/hv/netvsc_drv.c | 456 ----------------
|
|
drivers/staging/hv/rndis_filter.c | 855 ------------------------------
|
|
15 files changed, 3320 insertions(+), 3317 deletions(-)
|
|
create mode 100644 drivers/net/hyperv/Kconfig
|
|
create mode 100644 drivers/net/hyperv/Makefile
|
|
create mode 100644 drivers/net/hyperv/hyperv_net.h
|
|
create mode 100644 drivers/net/hyperv/netvsc.c
|
|
create mode 100644 drivers/net/hyperv/netvsc_drv.c
|
|
create mode 100644 drivers/net/hyperv/rndis_filter.c
|
|
delete mode 100644 drivers/staging/hv/hyperv_net.h
|
|
delete mode 100644 drivers/staging/hv/netvsc.c
|
|
delete mode 100644 drivers/staging/hv/netvsc_drv.c
|
|
delete mode 100644 drivers/staging/hv/rndis_filter.c
|
|
|
|
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
|
|
index 654a5e9..99aa7fa 100644
|
|
--- a/drivers/net/Kconfig
|
|
+++ b/drivers/net/Kconfig
|
|
@@ -338,4 +338,6 @@ config VMXNET3
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called vmxnet3.
|
|
|
|
+source "drivers/net/hyperv/Kconfig"
|
|
+
|
|
endif # NETDEVICES
|
|
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
|
|
index fa877cd..a81192b 100644
|
|
--- a/drivers/net/Makefile
|
|
+++ b/drivers/net/Makefile
|
|
@@ -66,3 +66,5 @@ obj-$(CONFIG_USB_USBNET) += usb/
|
|
obj-$(CONFIG_USB_ZD1201) += usb/
|
|
obj-$(CONFIG_USB_IPHETH) += usb/
|
|
obj-$(CONFIG_USB_CDC_PHONET) += usb/
|
|
+
|
|
+obj-$(CONFIG_HYPERV_NET) += hyperv/
|
|
diff --git a/drivers/net/hyperv/Kconfig b/drivers/net/hyperv/Kconfig
|
|
new file mode 100644
|
|
index 0000000..936968d
|
|
--- /dev/null
|
|
+++ b/drivers/net/hyperv/Kconfig
|
|
@@ -0,0 +1,5 @@
|
|
+config HYPERV_NET
|
|
+ tristate "Microsoft Hyper-V virtual network driver"
|
|
+ depends on HYPERV
|
|
+ help
|
|
+ Select this option to enable the Hyper-V virtual network driver.
|
|
diff --git a/drivers/net/hyperv/Makefile b/drivers/net/hyperv/Makefile
|
|
new file mode 100644
|
|
index 0000000..c8a6682
|
|
--- /dev/null
|
|
+++ b/drivers/net/hyperv/Makefile
|
|
@@ -0,0 +1,3 @@
|
|
+obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o
|
|
+
|
|
+hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o
|
|
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
|
|
new file mode 100644
|
|
index 0000000..ac1ec84
|
|
--- /dev/null
|
|
+++ b/drivers/net/hyperv/hyperv_net.h
|
|
@@ -0,0 +1,1058 @@
|
|
+/*
|
|
+ *
|
|
+ * Copyright (c) 2011, Microsoft Corporation.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms and conditions of the GNU General Public License,
|
|
+ * version 2, as published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Haiyang Zhang <haiyangz@microsoft.com>
|
|
+ * Hank Janssen <hjanssen@microsoft.com>
|
|
+ * K. Y. Srinivasan <kys@microsoft.com>
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _HYPERV_NET_H
|
|
+#define _HYPERV_NET_H
|
|
+
|
|
+#include <linux/list.h>
|
|
+#include <linux/hyperv.h>
|
|
+
|
|
+/* Fwd declaration */
|
|
+struct hv_netvsc_packet;
|
|
+
|
|
+/* Represent the xfer page packet which contains 1 or more netvsc packet */
|
|
+struct xferpage_packet {
|
|
+ struct list_head list_ent;
|
|
+
|
|
+ /* # of netvsc packets this xfer packet contains */
|
|
+ u32 count;
|
|
+};
|
|
+
|
|
+/* The number of pages which are enough to cover jumbo frame buffer. */
|
|
+#define NETVSC_PACKET_MAXPAGE 4
|
|
+
|
|
+/*
|
|
+ * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
|
|
+ * within the RNDIS
|
|
+ */
|
|
+struct hv_netvsc_packet {
|
|
+ /* Bookkeeping stuff */
|
|
+ struct list_head list_ent;
|
|
+
|
|
+ struct hv_device *device;
|
|
+ bool is_data_pkt;
|
|
+
|
|
+ /*
|
|
+ * Valid only for receives when we break a xfer page packet
|
|
+ * into multiple netvsc packets
|
|
+ */
|
|
+ struct xferpage_packet *xfer_page_pkt;
|
|
+
|
|
+ union {
|
|
+ struct {
|
|
+ u64 recv_completion_tid;
|
|
+ void *recv_completion_ctx;
|
|
+ void (*recv_completion)(void *context);
|
|
+ } recv;
|
|
+ struct {
|
|
+ u64 send_completion_tid;
|
|
+ void *send_completion_ctx;
|
|
+ void (*send_completion)(void *context);
|
|
+ } send;
|
|
+ } completion;
|
|
+
|
|
+ /* This points to the memory after page_buf */
|
|
+ void *extension;
|
|
+
|
|
+ u32 total_data_buflen;
|
|
+ /* Points to the send/receive buffer where the ethernet frame is */
|
|
+ u32 page_buf_cnt;
|
|
+ struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE];
|
|
+};
|
|
+
|
|
+struct netvsc_device_info {
|
|
+ unsigned char mac_adr[6];
|
|
+ bool link_state; /* 0 - link up, 1 - link down */
|
|
+ int ring_size;
|
|
+};
|
|
+
|
|
+/* Interface */
|
|
+int netvsc_device_add(struct hv_device *device, void *additional_info);
|
|
+int netvsc_device_remove(struct hv_device *device);
|
|
+int netvsc_send(struct hv_device *device,
|
|
+ struct hv_netvsc_packet *packet);
|
|
+void netvsc_linkstatus_callback(struct hv_device *device_obj,
|
|
+ unsigned int status);
|
|
+int netvsc_recv_callback(struct hv_device *device_obj,
|
|
+ struct hv_netvsc_packet *packet);
|
|
+int rndis_filter_open(struct hv_device *dev);
|
|
+int rndis_filter_close(struct hv_device *dev);
|
|
+int rndis_filter_device_add(struct hv_device *dev,
|
|
+ void *additional_info);
|
|
+void rndis_filter_device_remove(struct hv_device *dev);
|
|
+int rndis_filter_receive(struct hv_device *dev,
|
|
+ struct hv_netvsc_packet *pkt);
|
|
+
|
|
+
|
|
+
|
|
+int rndis_filter_send(struct hv_device *dev,
|
|
+ struct hv_netvsc_packet *pkt);
|
|
+
|
|
+#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF)
|
|
+
|
|
+#define NVSP_PROTOCOL_VERSION_1 2
|
|
+#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1
|
|
+#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1
|
|
+
|
|
+enum {
|
|
+ NVSP_MSG_TYPE_NONE = 0,
|
|
+
|
|
+ /* Init Messages */
|
|
+ NVSP_MSG_TYPE_INIT = 1,
|
|
+ NVSP_MSG_TYPE_INIT_COMPLETE = 2,
|
|
+
|
|
+ NVSP_VERSION_MSG_START = 100,
|
|
+
|
|
+ /* Version 1 Messages */
|
|
+ NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START,
|
|
+
|
|
+ NVSP_MSG1_TYPE_SEND_RECV_BUF,
|
|
+ NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE,
|
|
+ NVSP_MSG1_TYPE_REVOKE_RECV_BUF,
|
|
+
|
|
+ NVSP_MSG1_TYPE_SEND_SEND_BUF,
|
|
+ NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE,
|
|
+ NVSP_MSG1_TYPE_REVOKE_SEND_BUF,
|
|
+
|
|
+ NVSP_MSG1_TYPE_SEND_RNDIS_PKT,
|
|
+ NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE,
|
|
+
|
|
+ /*
|
|
+ * This should be set to the number of messages for the version with
|
|
+ * the maximum number of messages.
|
|
+ */
|
|
+ NVSP_NUM_MSG_PER_VERSION = 9,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ NVSP_STAT_NONE = 0,
|
|
+ NVSP_STAT_SUCCESS,
|
|
+ NVSP_STAT_FAIL,
|
|
+ NVSP_STAT_PROTOCOL_TOO_NEW,
|
|
+ NVSP_STAT_PROTOCOL_TOO_OLD,
|
|
+ NVSP_STAT_INVALID_RNDIS_PKT,
|
|
+ NVSP_STAT_BUSY,
|
|
+ NVSP_STAT_MAX,
|
|
+};
|
|
+
|
|
+struct nvsp_message_header {
|
|
+ u32 msg_type;
|
|
+};
|
|
+
|
|
+/* Init Messages */
|
|
+
|
|
+/*
|
|
+ * This message is used by the VSC to initialize the channel after the channels
|
|
+ * has been opened. This message should never include anything other then
|
|
+ * versioning (i.e. this message will be the same for ever).
|
|
+ */
|
|
+struct nvsp_message_init {
|
|
+ u32 min_protocol_ver;
|
|
+ u32 max_protocol_ver;
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * This message is used by the VSP to complete the initialization of the
|
|
+ * channel. This message should never include anything other then versioning
|
|
+ * (i.e. this message will be the same for ever).
|
|
+ */
|
|
+struct nvsp_message_init_complete {
|
|
+ u32 negotiated_protocol_ver;
|
|
+ u32 max_mdl_chain_len;
|
|
+ u32 status;
|
|
+} __packed;
|
|
+
|
|
+union nvsp_message_init_uber {
|
|
+ struct nvsp_message_init init;
|
|
+ struct nvsp_message_init_complete init_complete;
|
|
+} __packed;
|
|
+
|
|
+/* Version 1 Messages */
|
|
+
|
|
+/*
|
|
+ * This message is used by the VSC to send the NDIS version to the VSP. The VSP
|
|
+ * can use this information when handling OIDs sent by the VSC.
|
|
+ */
|
|
+struct nvsp_1_message_send_ndis_version {
|
|
+ u32 ndis_major_ver;
|
|
+ u32 ndis_minor_ver;
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * This message is used by the VSC to send a receive buffer to the VSP. The VSP
|
|
+ * can then use the receive buffer to send data to the VSC.
|
|
+ */
|
|
+struct nvsp_1_message_send_receive_buffer {
|
|
+ u32 gpadl_handle;
|
|
+ u16 id;
|
|
+} __packed;
|
|
+
|
|
+struct nvsp_1_receive_buffer_section {
|
|
+ u32 offset;
|
|
+ u32 sub_alloc_size;
|
|
+ u32 num_sub_allocs;
|
|
+ u32 end_offset;
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * This message is used by the VSP to acknowledge a receive buffer send by the
|
|
+ * VSC. This message must be sent by the VSP before the VSP uses the receive
|
|
+ * buffer.
|
|
+ */
|
|
+struct nvsp_1_message_send_receive_buffer_complete {
|
|
+ u32 status;
|
|
+ u32 num_sections;
|
|
+
|
|
+ /*
|
|
+ * The receive buffer is split into two parts, a large suballocation
|
|
+ * section and a small suballocation section. These sections are then
|
|
+ * suballocated by a certain size.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * For example, the following break up of the receive buffer has 6
|
|
+ * large suballocations and 10 small suballocations.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * | Large Section | | Small Section |
|
|
+ * ------------------------------------------------------------
|
|
+ * | | | | | | | | | | | | | | | | | |
|
|
+ * | |
|
|
+ * LargeOffset SmallOffset
|
|
+ */
|
|
+
|
|
+ struct nvsp_1_receive_buffer_section sections[1];
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * This message is sent by the VSC to revoke the receive buffer. After the VSP
|
|
+ * completes this transaction, the vsp should never use the receive buffer
|
|
+ * again.
|
|
+ */
|
|
+struct nvsp_1_message_revoke_receive_buffer {
|
|
+ u16 id;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * This message is used by the VSC to send a send buffer to the VSP. The VSC
|
|
+ * can then use the send buffer to send data to the VSP.
|
|
+ */
|
|
+struct nvsp_1_message_send_send_buffer {
|
|
+ u32 gpadl_handle;
|
|
+ u16 id;
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * This message is used by the VSP to acknowledge a send buffer sent by the
|
|
+ * VSC. This message must be sent by the VSP before the VSP uses the sent
|
|
+ * buffer.
|
|
+ */
|
|
+struct nvsp_1_message_send_send_buffer_complete {
|
|
+ u32 status;
|
|
+
|
|
+ /*
|
|
+ * The VSC gets to choose the size of the send buffer and the VSP gets
|
|
+ * to choose the sections size of the buffer. This was done to enable
|
|
+ * dynamic reconfigurations when the cost of GPA-direct buffers
|
|
+ * decreases.
|
|
+ */
|
|
+ u32 section_size;
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * This message is sent by the VSC to revoke the send buffer. After the VSP
|
|
+ * completes this transaction, the vsp should never use the send buffer again.
|
|
+ */
|
|
+struct nvsp_1_message_revoke_send_buffer {
|
|
+ u16 id;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * This message is used by both the VSP and the VSC to send a RNDIS message to
|
|
+ * the opposite channel endpoint.
|
|
+ */
|
|
+struct nvsp_1_message_send_rndis_packet {
|
|
+ /*
|
|
+ * This field is specified by RNIDS. They assume there's two different
|
|
+ * channels of communication. However, the Network VSP only has one.
|
|
+ * Therefore, the channel travels with the RNDIS packet.
|
|
+ */
|
|
+ u32 channel_type;
|
|
+
|
|
+ /*
|
|
+ * This field is used to send part or all of the data through a send
|
|
+ * buffer. This values specifies an index into the send buffer. If the
|
|
+ * index is 0xFFFFFFFF, then the send buffer is not being used and all
|
|
+ * of the data was sent through other VMBus mechanisms.
|
|
+ */
|
|
+ u32 send_buf_section_index;
|
|
+ u32 send_buf_section_size;
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * This message is used by both the VSP and the VSC to complete a RNDIS message
|
|
+ * to the opposite channel endpoint. At this point, the initiator of this
|
|
+ * message cannot use any resources associated with the original RNDIS packet.
|
|
+ */
|
|
+struct nvsp_1_message_send_rndis_packet_complete {
|
|
+ u32 status;
|
|
+};
|
|
+
|
|
+union nvsp_1_message_uber {
|
|
+ struct nvsp_1_message_send_ndis_version send_ndis_ver;
|
|
+
|
|
+ struct nvsp_1_message_send_receive_buffer send_recv_buf;
|
|
+ struct nvsp_1_message_send_receive_buffer_complete
|
|
+ send_recv_buf_complete;
|
|
+ struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf;
|
|
+
|
|
+ struct nvsp_1_message_send_send_buffer send_send_buf;
|
|
+ struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete;
|
|
+ struct nvsp_1_message_revoke_send_buffer revoke_send_buf;
|
|
+
|
|
+ struct nvsp_1_message_send_rndis_packet send_rndis_pkt;
|
|
+ struct nvsp_1_message_send_rndis_packet_complete
|
|
+ send_rndis_pkt_complete;
|
|
+} __packed;
|
|
+
|
|
+union nvsp_all_messages {
|
|
+ union nvsp_message_init_uber init_msg;
|
|
+ union nvsp_1_message_uber v1_msg;
|
|
+} __packed;
|
|
+
|
|
+/* ALL Messages */
|
|
+struct nvsp_message {
|
|
+ struct nvsp_message_header hdr;
|
|
+ union nvsp_all_messages msg;
|
|
+} __packed;
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+/* #define NVSC_MIN_PROTOCOL_VERSION 1 */
|
|
+/* #define NVSC_MAX_PROTOCOL_VERSION 1 */
|
|
+
|
|
+#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */
|
|
+
|
|
+#define NETVSC_RECEIVE_BUFFER_ID 0xcafe
|
|
+
|
|
+#define NETVSC_RECEIVE_SG_COUNT 1
|
|
+
|
|
+/* Preallocated receive packets */
|
|
+#define NETVSC_RECEIVE_PACKETLIST_COUNT 256
|
|
+
|
|
+#define NETVSC_PACKET_SIZE 2048
|
|
+
|
|
+/* Per netvsc channel-specific */
|
|
+struct netvsc_device {
|
|
+ struct hv_device *dev;
|
|
+
|
|
+ atomic_t num_outstanding_sends;
|
|
+ bool destroy;
|
|
+ /*
|
|
+ * List of free preallocated hv_netvsc_packet to represent receive
|
|
+ * packet
|
|
+ */
|
|
+ struct list_head recv_pkt_list;
|
|
+ spinlock_t recv_pkt_list_lock;
|
|
+
|
|
+ /* Receive buffer allocated by us but manages by NetVSP */
|
|
+ void *recv_buf;
|
|
+ u32 recv_buf_size;
|
|
+ u32 recv_buf_gpadl_handle;
|
|
+ u32 recv_section_cnt;
|
|
+ struct nvsp_1_receive_buffer_section *recv_section;
|
|
+
|
|
+ /* Used for NetVSP initialization protocol */
|
|
+ struct completion channel_init_wait;
|
|
+ struct nvsp_message channel_init_pkt;
|
|
+
|
|
+ struct nvsp_message revoke_packet;
|
|
+ /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */
|
|
+
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ /* Holds rndis device info */
|
|
+ void *extension;
|
|
+};
|
|
+
|
|
+
|
|
+/* Status codes */
|
|
+
|
|
+
|
|
+#ifndef STATUS_SUCCESS
|
|
+#define STATUS_SUCCESS (0x00000000L)
|
|
+#endif
|
|
+
|
|
+#ifndef STATUS_UNSUCCESSFUL
|
|
+#define STATUS_UNSUCCESSFUL (0xC0000001L)
|
|
+#endif
|
|
+
|
|
+#ifndef STATUS_PENDING
|
|
+#define STATUS_PENDING (0x00000103L)
|
|
+#endif
|
|
+
|
|
+#ifndef STATUS_INSUFFICIENT_RESOURCES
|
|
+#define STATUS_INSUFFICIENT_RESOURCES (0xC000009AL)
|
|
+#endif
|
|
+
|
|
+#ifndef STATUS_BUFFER_OVERFLOW
|
|
+#define STATUS_BUFFER_OVERFLOW (0x80000005L)
|
|
+#endif
|
|
+
|
|
+#ifndef STATUS_NOT_SUPPORTED
|
|
+#define STATUS_NOT_SUPPORTED (0xC00000BBL)
|
|
+#endif
|
|
+
|
|
+#define RNDIS_STATUS_SUCCESS (STATUS_SUCCESS)
|
|
+#define RNDIS_STATUS_PENDING (STATUS_PENDING)
|
|
+#define RNDIS_STATUS_NOT_RECOGNIZED (0x00010001L)
|
|
+#define RNDIS_STATUS_NOT_COPIED (0x00010002L)
|
|
+#define RNDIS_STATUS_NOT_ACCEPTED (0x00010003L)
|
|
+#define RNDIS_STATUS_CALL_ACTIVE (0x00010007L)
|
|
+
|
|
+#define RNDIS_STATUS_ONLINE (0x40010003L)
|
|
+#define RNDIS_STATUS_RESET_START (0x40010004L)
|
|
+#define RNDIS_STATUS_RESET_END (0x40010005L)
|
|
+#define RNDIS_STATUS_RING_STATUS (0x40010006L)
|
|
+#define RNDIS_STATUS_CLOSED (0x40010007L)
|
|
+#define RNDIS_STATUS_WAN_LINE_UP (0x40010008L)
|
|
+#define RNDIS_STATUS_WAN_LINE_DOWN (0x40010009L)
|
|
+#define RNDIS_STATUS_WAN_FRAGMENT (0x4001000AL)
|
|
+#define RNDIS_STATUS_MEDIA_CONNECT (0x4001000BL)
|
|
+#define RNDIS_STATUS_MEDIA_DISCONNECT (0x4001000CL)
|
|
+#define RNDIS_STATUS_HARDWARE_LINE_UP (0x4001000DL)
|
|
+#define RNDIS_STATUS_HARDWARE_LINE_DOWN (0x4001000EL)
|
|
+#define RNDIS_STATUS_INTERFACE_UP (0x4001000FL)
|
|
+#define RNDIS_STATUS_INTERFACE_DOWN (0x40010010L)
|
|
+#define RNDIS_STATUS_MEDIA_BUSY (0x40010011L)
|
|
+#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION (0x40010012L)
|
|
+#define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION
|
|
+#define RNDIS_STATUS_LINK_SPEED_CHANGE (0x40010013L)
|
|
+
|
|
+#define RNDIS_STATUS_NOT_RESETTABLE (0x80010001L)
|
|
+#define RNDIS_STATUS_SOFT_ERRORS (0x80010003L)
|
|
+#define RNDIS_STATUS_HARD_ERRORS (0x80010004L)
|
|
+#define RNDIS_STATUS_BUFFER_OVERFLOW (STATUS_BUFFER_OVERFLOW)
|
|
+
|
|
+#define RNDIS_STATUS_FAILURE (STATUS_UNSUCCESSFUL)
|
|
+#define RNDIS_STATUS_RESOURCES (STATUS_INSUFFICIENT_RESOURCES)
|
|
+#define RNDIS_STATUS_CLOSING (0xC0010002L)
|
|
+#define RNDIS_STATUS_BAD_VERSION (0xC0010004L)
|
|
+#define RNDIS_STATUS_BAD_CHARACTERISTICS (0xC0010005L)
|
|
+#define RNDIS_STATUS_ADAPTER_NOT_FOUND (0xC0010006L)
|
|
+#define RNDIS_STATUS_OPEN_FAILED (0xC0010007L)
|
|
+#define RNDIS_STATUS_DEVICE_FAILED (0xC0010008L)
|
|
+#define RNDIS_STATUS_MULTICAST_FULL (0xC0010009L)
|
|
+#define RNDIS_STATUS_MULTICAST_EXISTS (0xC001000AL)
|
|
+#define RNDIS_STATUS_MULTICAST_NOT_FOUND (0xC001000BL)
|
|
+#define RNDIS_STATUS_REQUEST_ABORTED (0xC001000CL)
|
|
+#define RNDIS_STATUS_RESET_IN_PROGRESS (0xC001000DL)
|
|
+#define RNDIS_STATUS_CLOSING_INDICATING (0xC001000EL)
|
|
+#define RNDIS_STATUS_NOT_SUPPORTED (STATUS_NOT_SUPPORTED)
|
|
+#define RNDIS_STATUS_INVALID_PACKET (0xC001000FL)
|
|
+#define RNDIS_STATUS_OPEN_LIST_FULL (0xC0010010L)
|
|
+#define RNDIS_STATUS_ADAPTER_NOT_READY (0xC0010011L)
|
|
+#define RNDIS_STATUS_ADAPTER_NOT_OPEN (0xC0010012L)
|
|
+#define RNDIS_STATUS_NOT_INDICATING (0xC0010013L)
|
|
+#define RNDIS_STATUS_INVALID_LENGTH (0xC0010014L)
|
|
+#define RNDIS_STATUS_INVALID_DATA (0xC0010015L)
|
|
+#define RNDIS_STATUS_BUFFER_TOO_SHORT (0xC0010016L)
|
|
+#define RNDIS_STATUS_INVALID_OID (0xC0010017L)
|
|
+#define RNDIS_STATUS_ADAPTER_REMOVED (0xC0010018L)
|
|
+#define RNDIS_STATUS_UNSUPPORTED_MEDIA (0xC0010019L)
|
|
+#define RNDIS_STATUS_GROUP_ADDRESS_IN_USE (0xC001001AL)
|
|
+#define RNDIS_STATUS_FILE_NOT_FOUND (0xC001001BL)
|
|
+#define RNDIS_STATUS_ERROR_READING_FILE (0xC001001CL)
|
|
+#define RNDIS_STATUS_ALREADY_MAPPED (0xC001001DL)
|
|
+#define RNDIS_STATUS_RESOURCE_CONFLICT (0xC001001EL)
|
|
+#define RNDIS_STATUS_NO_CABLE (0xC001001FL)
|
|
+
|
|
+#define RNDIS_STATUS_INVALID_SAP (0xC0010020L)
|
|
+#define RNDIS_STATUS_SAP_IN_USE (0xC0010021L)
|
|
+#define RNDIS_STATUS_INVALID_ADDRESS (0xC0010022L)
|
|
+#define RNDIS_STATUS_VC_NOT_ACTIVATED (0xC0010023L)
|
|
+#define RNDIS_STATUS_DEST_OUT_OF_ORDER (0xC0010024L)
|
|
+#define RNDIS_STATUS_VC_NOT_AVAILABLE (0xC0010025L)
|
|
+#define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE (0xC0010026L)
|
|
+#define RNDIS_STATUS_INCOMPATABLE_QOS (0xC0010027L)
|
|
+#define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED (0xC0010028L)
|
|
+#define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION (0xC0010029L)
|
|
+
|
|
+#define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR (0xC0011000L)
|
|
+
|
|
+/* Object Identifiers used by NdisRequest Query/Set Information */
|
|
+/* General Objects */
|
|
+#define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101
|
|
+#define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102
|
|
+#define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103
|
|
+#define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104
|
|
+#define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
|
|
+#define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
|
|
+#define RNDIS_OID_GEN_LINK_SPEED 0x00010107
|
|
+#define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
|
|
+#define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
|
|
+#define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
|
|
+#define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
|
|
+#define RNDIS_OID_GEN_VENDOR_ID 0x0001010C
|
|
+#define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D
|
|
+#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
|
|
+#define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
|
|
+#define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110
|
|
+#define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
|
|
+#define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112
|
|
+#define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113
|
|
+#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
|
|
+#define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
|
|
+#define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
|
|
+#define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
|
|
+#define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
|
|
+#define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A
|
|
+#define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
|
|
+
|
|
+#define RNDIS_OID_GEN_XMIT_OK 0x00020101
|
|
+#define RNDIS_OID_GEN_RCV_OK 0x00020102
|
|
+#define RNDIS_OID_GEN_XMIT_ERROR 0x00020103
|
|
+#define RNDIS_OID_GEN_RCV_ERROR 0x00020104
|
|
+#define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105
|
|
+
|
|
+#define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
|
|
+#define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
|
|
+#define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
|
|
+#define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
|
|
+#define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
|
|
+#define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
|
|
+#define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207
|
|
+#define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
|
|
+#define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209
|
|
+#define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
|
|
+#define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
|
|
+#define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
|
|
+
|
|
+#define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D
|
|
+#define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
|
|
+
|
|
+#define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F
|
|
+#define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210
|
|
+
|
|
+/* These are connection-oriented general OIDs. */
|
|
+/* These replace the above OIDs for connection-oriented media. */
|
|
+#define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101
|
|
+#define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102
|
|
+#define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103
|
|
+#define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104
|
|
+#define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105
|
|
+#define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106
|
|
+#define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107
|
|
+#define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108
|
|
+#define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109
|
|
+#define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A
|
|
+#define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B
|
|
+#define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C
|
|
+#define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D
|
|
+
|
|
+#define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201
|
|
+#define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202
|
|
+
|
|
+/* These are connection-oriented statistics OIDs. */
|
|
+#define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101
|
|
+#define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102
|
|
+#define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103
|
|
+#define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104
|
|
+#define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105
|
|
+
|
|
+
|
|
+#define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201
|
|
+#define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202
|
|
+#define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203
|
|
+#define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204
|
|
+#define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205
|
|
+#define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206
|
|
+
|
|
+/* These are objects for Connection-oriented media call-managers. */
|
|
+#define RNDIS_OID_CO_ADD_PVC 0xFF000001
|
|
+#define RNDIS_OID_CO_DELETE_PVC 0xFF000002
|
|
+#define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003
|
|
+#define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004
|
|
+#define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005
|
|
+#define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006
|
|
+#define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007
|
|
+#define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008
|
|
+#define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009
|
|
+
|
|
+/* 802.3 Objects (Ethernet) */
|
|
+#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101
|
|
+#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102
|
|
+#define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103
|
|
+#define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
|
|
+#define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105
|
|
+
|
|
+#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
|
|
+
|
|
+#define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
|
|
+#define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102
|
|
+#define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
|
|
+
|
|
+#define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201
|
|
+#define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
|
|
+#define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203
|
|
+#define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204
|
|
+#define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
|
|
+#define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
|
|
+#define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
|
|
+
|
|
+/* Remote NDIS message types */
|
|
+#define REMOTE_NDIS_PACKET_MSG 0x00000001
|
|
+#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002
|
|
+#define REMOTE_NDIS_HALT_MSG 0x00000003
|
|
+#define REMOTE_NDIS_QUERY_MSG 0x00000004
|
|
+#define REMOTE_NDIS_SET_MSG 0x00000005
|
|
+#define REMOTE_NDIS_RESET_MSG 0x00000006
|
|
+#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007
|
|
+#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008
|
|
+
|
|
+#define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001
|
|
+#define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002
|
|
+#define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005
|
|
+#define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006
|
|
+#define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007
|
|
+
|
|
+/* Remote NDIS message completion types */
|
|
+#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002
|
|
+#define REMOTE_NDIS_QUERY_CMPLT 0x80000004
|
|
+#define REMOTE_NDIS_SET_CMPLT 0x80000005
|
|
+#define REMOTE_NDIS_RESET_CMPLT 0x80000006
|
|
+#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008
|
|
+
|
|
+#define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001
|
|
+#define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002
|
|
+#define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005
|
|
+#define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006
|
|
+
|
|
+/*
|
|
+ * Reserved message type for private communication between lower-layer host
|
|
+ * driver and remote device, if necessary.
|
|
+ */
|
|
+#define REMOTE_NDIS_BUS_MSG 0xff000001
|
|
+
|
|
+/* Defines for DeviceFlags in struct rndis_initialize_complete */
|
|
+#define RNDIS_DF_CONNECTIONLESS 0x00000001
|
|
+#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002
|
|
+#define RNDIS_DF_RAW_DATA 0x00000004
|
|
+
|
|
+/* Remote NDIS medium types. */
|
|
+#define RNDIS_MEDIUM_802_3 0x00000000
|
|
+#define RNDIS_MEDIUM_802_5 0x00000001
|
|
+#define RNDIS_MEDIUM_FDDI 0x00000002
|
|
+#define RNDIS_MEDIUM_WAN 0x00000003
|
|
+#define RNDIS_MEDIUM_LOCAL_TALK 0x00000004
|
|
+#define RNDIS_MEDIUM_ARCNET_RAW 0x00000006
|
|
+#define RNDIS_MEDIUM_ARCNET_878_2 0x00000007
|
|
+#define RNDIS_MEDIUM_ATM 0x00000008
|
|
+#define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009
|
|
+#define RNDIS_MEDIUM_IRDA 0x0000000a
|
|
+#define RNDIS_MEDIUM_CO_WAN 0x0000000b
|
|
+/* Not a real medium, defined as an upper-bound */
|
|
+#define RNDIS_MEDIUM_MAX 0x0000000d
|
|
+
|
|
+
|
|
+/* Remote NDIS medium connection states. */
|
|
+#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000
|
|
+#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001
|
|
+
|
|
+/* Remote NDIS version numbers */
|
|
+#define RNDIS_MAJOR_VERSION 0x00000001
|
|
+#define RNDIS_MINOR_VERSION 0x00000000
|
|
+
|
|
+
|
|
+/* NdisInitialize message */
|
|
+struct rndis_initialize_request {
|
|
+ u32 req_id;
|
|
+ u32 major_ver;
|
|
+ u32 minor_ver;
|
|
+ u32 max_xfer_size;
|
|
+};
|
|
+
|
|
+/* Response to NdisInitialize */
|
|
+struct rndis_initialize_complete {
|
|
+ u32 req_id;
|
|
+ u32 status;
|
|
+ u32 major_ver;
|
|
+ u32 minor_ver;
|
|
+ u32 dev_flags;
|
|
+ u32 medium;
|
|
+ u32 max_pkt_per_msg;
|
|
+ u32 max_xfer_size;
|
|
+ u32 pkt_alignment_factor;
|
|
+ u32 af_list_offset;
|
|
+ u32 af_list_size;
|
|
+};
|
|
+
|
|
+/* Call manager devices only: Information about an address family */
|
|
+/* supported by the device is appended to the response to NdisInitialize. */
|
|
+struct rndis_co_address_family {
|
|
+ u32 address_family;
|
|
+ u32 major_ver;
|
|
+ u32 minor_ver;
|
|
+};
|
|
+
|
|
+/* NdisHalt message */
|
|
+struct rndis_halt_request {
|
|
+ u32 req_id;
|
|
+};
|
|
+
|
|
+/* NdisQueryRequest message */
|
|
+struct rndis_query_request {
|
|
+ u32 req_id;
|
|
+ u32 oid;
|
|
+ u32 info_buflen;
|
|
+ u32 info_buf_offset;
|
|
+ u32 dev_vc_handle;
|
|
+};
|
|
+
|
|
+/* Response to NdisQueryRequest */
|
|
+struct rndis_query_complete {
|
|
+ u32 req_id;
|
|
+ u32 status;
|
|
+ u32 info_buflen;
|
|
+ u32 info_buf_offset;
|
|
+};
|
|
+
|
|
+/* NdisSetRequest message */
|
|
+struct rndis_set_request {
|
|
+ u32 req_id;
|
|
+ u32 oid;
|
|
+ u32 info_buflen;
|
|
+ u32 info_buf_offset;
|
|
+ u32 dev_vc_handle;
|
|
+};
|
|
+
|
|
+/* Response to NdisSetRequest */
|
|
+struct rndis_set_complete {
|
|
+ u32 req_id;
|
|
+ u32 status;
|
|
+};
|
|
+
|
|
+/* NdisReset message */
|
|
+struct rndis_reset_request {
|
|
+ u32 reserved;
|
|
+};
|
|
+
|
|
+/* Response to NdisReset */
|
|
+struct rndis_reset_complete {
|
|
+ u32 status;
|
|
+ u32 addressing_reset;
|
|
+};
|
|
+
|
|
+/* NdisMIndicateStatus message */
|
|
+struct rndis_indicate_status {
|
|
+ u32 status;
|
|
+ u32 status_buflen;
|
|
+ u32 status_buf_offset;
|
|
+};
|
|
+
|
|
+/* Diagnostic information passed as the status buffer in */
|
|
+/* struct rndis_indicate_status messages signifying error conditions. */
|
|
+struct rndis_diagnostic_info {
|
|
+ u32 diag_status;
|
|
+ u32 error_offset;
|
|
+};
|
|
+
|
|
+/* NdisKeepAlive message */
|
|
+struct rndis_keepalive_request {
|
|
+ u32 req_id;
|
|
+};
|
|
+
|
|
+/* Response to NdisKeepAlive */
|
|
+struct rndis_keepalive_complete {
|
|
+ u32 req_id;
|
|
+ u32 status;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Data message. All Offset fields contain byte offsets from the beginning of
|
|
+ * struct rndis_packet. All Length fields are in bytes. VcHandle is set
|
|
+ * to 0 for connectionless data, otherwise it contains the VC handle.
|
|
+ */
|
|
+struct rndis_packet {
|
|
+ u32 data_offset;
|
|
+ u32 data_len;
|
|
+ u32 oob_data_offset;
|
|
+ u32 oob_data_len;
|
|
+ u32 num_oob_data_elements;
|
|
+ u32 per_pkt_info_offset;
|
|
+ u32 per_pkt_info_len;
|
|
+ u32 vc_handle;
|
|
+ u32 reserved;
|
|
+};
|
|
+
|
|
+/* Optional Out of Band data associated with a Data message. */
|
|
+struct rndis_oobd {
|
|
+ u32 size;
|
|
+ u32 type;
|
|
+ u32 class_info_offset;
|
|
+};
|
|
+
|
|
+/* Packet extension field contents associated with a Data message. */
|
|
+struct rndis_per_packet_info {
|
|
+ u32 size;
|
|
+ u32 type;
|
|
+ u32 per_pkt_info_offset;
|
|
+};
|
|
+
|
|
+/* Format of Information buffer passed in a SetRequest for the OID */
|
|
+/* OID_GEN_RNDIS_CONFIG_PARAMETER. */
|
|
+struct rndis_config_parameter_info {
|
|
+ u32 parameter_name_offset;
|
|
+ u32 parameter_name_length;
|
|
+ u32 parameter_type;
|
|
+ u32 parameter_value_offset;
|
|
+ u32 parameter_value_length;
|
|
+};
|
|
+
|
|
+/* Values for ParameterType in struct rndis_config_parameter_info */
|
|
+#define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0
|
|
+#define RNDIS_CONFIG_PARAM_TYPE_STRING 2
|
|
+
|
|
+/* CONDIS Miniport messages for connection oriented devices */
|
|
+/* that do not implement a call manager. */
|
|
+
|
|
+/* CoNdisMiniportCreateVc message */
|
|
+struct rcondis_mp_create_vc {
|
|
+ u32 req_id;
|
|
+ u32 ndis_vc_handle;
|
|
+};
|
|
+
|
|
+/* Response to CoNdisMiniportCreateVc */
|
|
+struct rcondis_mp_create_vc_complete {
|
|
+ u32 req_id;
|
|
+ u32 dev_vc_handle;
|
|
+ u32 status;
|
|
+};
|
|
+
|
|
+/* CoNdisMiniportDeleteVc message */
|
|
+struct rcondis_mp_delete_vc {
|
|
+ u32 req_id;
|
|
+ u32 dev_vc_handle;
|
|
+};
|
|
+
|
|
+/* Response to CoNdisMiniportDeleteVc */
|
|
+struct rcondis_mp_delete_vc_complete {
|
|
+ u32 req_id;
|
|
+ u32 status;
|
|
+};
|
|
+
|
|
+/* CoNdisMiniportQueryRequest message */
|
|
+struct rcondis_mp_query_request {
|
|
+ u32 req_id;
|
|
+ u32 request_type;
|
|
+ u32 oid;
|
|
+ u32 dev_vc_handle;
|
|
+ u32 info_buflen;
|
|
+ u32 info_buf_offset;
|
|
+};
|
|
+
|
|
+/* CoNdisMiniportSetRequest message */
|
|
+struct rcondis_mp_set_request {
|
|
+ u32 req_id;
|
|
+ u32 request_type;
|
|
+ u32 oid;
|
|
+ u32 dev_vc_handle;
|
|
+ u32 info_buflen;
|
|
+ u32 info_buf_offset;
|
|
+};
|
|
+
|
|
+/* CoNdisIndicateStatus message */
|
|
+struct rcondis_indicate_status {
|
|
+ u32 ndis_vc_handle;
|
|
+ u32 status;
|
|
+ u32 status_buflen;
|
|
+ u32 status_buf_offset;
|
|
+};
|
|
+
|
|
+/* CONDIS Call/VC parameters */
|
|
+struct rcondis_specific_parameters {
|
|
+ u32 parameter_type;
|
|
+ u32 parameter_length;
|
|
+ u32 parameter_lffset;
|
|
+};
|
|
+
|
|
+struct rcondis_media_parameters {
|
|
+ u32 flags;
|
|
+ u32 reserved1;
|
|
+ u32 reserved2;
|
|
+ struct rcondis_specific_parameters media_specific;
|
|
+};
|
|
+
|
|
+struct rndis_flowspec {
|
|
+ u32 token_rate;
|
|
+ u32 token_bucket_size;
|
|
+ u32 peak_bandwidth;
|
|
+ u32 latency;
|
|
+ u32 delay_variation;
|
|
+ u32 service_type;
|
|
+ u32 max_sdu_size;
|
|
+ u32 minimum_policed_size;
|
|
+};
|
|
+
|
|
+struct rcondis_call_manager_parameters {
|
|
+ struct rndis_flowspec transmit;
|
|
+ struct rndis_flowspec receive;
|
|
+ struct rcondis_specific_parameters call_mgr_specific;
|
|
+};
|
|
+
|
|
+/* CoNdisMiniportActivateVc message */
|
|
+struct rcondis_mp_activate_vc_request {
|
|
+ u32 req_id;
|
|
+ u32 flags;
|
|
+ u32 dev_vc_handle;
|
|
+ u32 media_params_offset;
|
|
+ u32 media_params_length;
|
|
+ u32 call_mgr_params_offset;
|
|
+ u32 call_mgr_params_length;
|
|
+};
|
|
+
|
|
+/* Response to CoNdisMiniportActivateVc */
|
|
+struct rcondis_mp_activate_vc_complete {
|
|
+ u32 req_id;
|
|
+ u32 status;
|
|
+};
|
|
+
|
|
+/* CoNdisMiniportDeactivateVc message */
|
|
+struct rcondis_mp_deactivate_vc_request {
|
|
+ u32 req_id;
|
|
+ u32 flags;
|
|
+ u32 dev_vc_handle;
|
|
+};
|
|
+
|
|
+/* Response to CoNdisMiniportDeactivateVc */
|
|
+struct rcondis_mp_deactivate_vc_complete {
|
|
+ u32 req_id;
|
|
+ u32 status;
|
|
+};
|
|
+
|
|
+
|
|
+/* union with all of the RNDIS messages */
|
|
+union rndis_message_container {
|
|
+ struct rndis_packet pkt;
|
|
+ struct rndis_initialize_request init_req;
|
|
+ struct rndis_halt_request halt_req;
|
|
+ struct rndis_query_request query_req;
|
|
+ struct rndis_set_request set_req;
|
|
+ struct rndis_reset_request reset_req;
|
|
+ struct rndis_keepalive_request keep_alive_req;
|
|
+ struct rndis_indicate_status indicate_status;
|
|
+ struct rndis_initialize_complete init_complete;
|
|
+ struct rndis_query_complete query_complete;
|
|
+ struct rndis_set_complete set_complete;
|
|
+ struct rndis_reset_complete reset_complete;
|
|
+ struct rndis_keepalive_complete keep_alive_complete;
|
|
+ struct rcondis_mp_create_vc co_miniport_create_vc;
|
|
+ struct rcondis_mp_delete_vc co_miniport_delete_vc;
|
|
+ struct rcondis_indicate_status co_indicate_status;
|
|
+ struct rcondis_mp_activate_vc_request co_miniport_activate_vc;
|
|
+ struct rcondis_mp_deactivate_vc_request co_miniport_deactivate_vc;
|
|
+ struct rcondis_mp_create_vc_complete co_miniport_create_vc_complete;
|
|
+ struct rcondis_mp_delete_vc_complete co_miniport_delete_vc_complete;
|
|
+ struct rcondis_mp_activate_vc_complete co_miniport_activate_vc_complete;
|
|
+ struct rcondis_mp_deactivate_vc_complete
|
|
+ co_miniport_deactivate_vc_complete;
|
|
+};
|
|
+
|
|
+/* Remote NDIS message format */
|
|
+struct rndis_message {
|
|
+ u32 ndis_msg_type;
|
|
+
|
|
+ /* Total length of this message, from the beginning */
|
|
+ /* of the sruct rndis_message, in bytes. */
|
|
+ u32 msg_len;
|
|
+
|
|
+ /* Actual message */
|
|
+ union rndis_message_container msg;
|
|
+};
|
|
+
|
|
+
|
|
+struct rndis_filter_packet {
|
|
+ void *completion_ctx;
|
|
+ void (*completion)(void *context);
|
|
+ struct rndis_message msg;
|
|
+};
|
|
+
|
|
+/* Handy macros */
|
|
+
|
|
+/* get the size of an RNDIS message. Pass in the message type, */
|
|
+/* struct rndis_set_request, struct rndis_packet for example */
|
|
+#define RNDIS_MESSAGE_SIZE(msg) \
|
|
+ (sizeof(msg) + (sizeof(struct rndis_message) - \
|
|
+ sizeof(union rndis_message_container)))
|
|
+
|
|
+/* get pointer to info buffer with message pointer */
|
|
+#define MESSAGE_TO_INFO_BUFFER(msg) \
|
|
+ (((unsigned char *)(msg)) + msg->info_buf_offset)
|
|
+
|
|
+/* get pointer to status buffer with message pointer */
|
|
+#define MESSAGE_TO_STATUS_BUFFER(msg) \
|
|
+ (((unsigned char *)(msg)) + msg->status_buf_offset)
|
|
+
|
|
+/* get pointer to OOBD buffer with message pointer */
|
|
+#define MESSAGE_TO_OOBD_BUFFER(msg) \
|
|
+ (((unsigned char *)(msg)) + msg->oob_data_offset)
|
|
+
|
|
+/* get pointer to data buffer with message pointer */
|
|
+#define MESSAGE_TO_DATA_BUFFER(msg) \
|
|
+ (((unsigned char *)(msg)) + msg->per_pkt_info_offset)
|
|
+
|
|
+/* get pointer to contained message from NDIS_MESSAGE pointer */
|
|
+#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(rndis_msg) \
|
|
+ ((void *) &rndis_msg->msg)
|
|
+
|
|
+/* get pointer to contained message from NDIS_MESSAGE pointer */
|
|
+#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_msg) \
|
|
+ ((void *) rndis_msg)
|
|
+
|
|
+
|
|
+#define __struct_bcount(x)
|
|
+
|
|
+
|
|
+
|
|
+#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \
|
|
+ sizeof(union rndis_message_container))
|
|
+
|
|
+#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
|
|
+#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
|
|
+#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
|
|
+#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
|
|
+#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
|
|
+#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
|
|
+#define NDIS_PACKET_TYPE_SMT 0x00000040
|
|
+#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
|
|
+#define NDIS_PACKET_TYPE_GROUP 0x00000100
|
|
+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
|
|
+#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
|
|
+#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
|
|
+
|
|
+
|
|
+
|
|
+#endif /* _HYPERV_NET_H */
|
|
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
|
|
new file mode 100644
|
|
index 0000000..28e69a6
|
|
--- /dev/null
|
|
+++ b/drivers/net/hyperv/netvsc.c
|
|
@@ -0,0 +1,939 @@
|
|
+/*
|
|
+ * Copyright (c) 2009, Microsoft Corporation.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms and conditions of the GNU General Public License,
|
|
+ * version 2, as published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Haiyang Zhang <haiyangz@microsoft.com>
|
|
+ * Hank Janssen <hjanssen@microsoft.com>
|
|
+ */
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/netdevice.h>
|
|
+
|
|
+#include "hyperv_net.h"
|
|
+
|
|
+
|
|
+static struct netvsc_device *alloc_net_device(struct hv_device *device)
|
|
+{
|
|
+ struct netvsc_device *net_device;
|
|
+ struct net_device *ndev = hv_get_drvdata(device);
|
|
+
|
|
+ net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
|
|
+ if (!net_device)
|
|
+ return NULL;
|
|
+
|
|
+
|
|
+ net_device->destroy = false;
|
|
+ net_device->dev = device;
|
|
+ net_device->ndev = ndev;
|
|
+
|
|
+ hv_set_drvdata(device, net_device);
|
|
+ return net_device;
|
|
+}
|
|
+
|
|
+static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
|
|
+{
|
|
+ struct netvsc_device *net_device;
|
|
+
|
|
+ net_device = hv_get_drvdata(device);
|
|
+ if (net_device && net_device->destroy)
|
|
+ net_device = NULL;
|
|
+
|
|
+ return net_device;
|
|
+}
|
|
+
|
|
+static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
|
|
+{
|
|
+ struct netvsc_device *net_device;
|
|
+
|
|
+ net_device = hv_get_drvdata(device);
|
|
+
|
|
+ if (!net_device)
|
|
+ goto get_in_err;
|
|
+
|
|
+ if (net_device->destroy &&
|
|
+ atomic_read(&net_device->num_outstanding_sends) == 0)
|
|
+ net_device = NULL;
|
|
+
|
|
+get_in_err:
|
|
+ return net_device;
|
|
+}
|
|
+
|
|
+
|
|
+static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
|
|
+{
|
|
+ struct nvsp_message *revoke_packet;
|
|
+ int ret = 0;
|
|
+ struct net_device *ndev = net_device->ndev;
|
|
+
|
|
+ /*
|
|
+ * If we got a section count, it means we received a
|
|
+ * SendReceiveBufferComplete msg (ie sent
|
|
+ * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need
|
|
+ * to send a revoke msg here
|
|
+ */
|
|
+ if (net_device->recv_section_cnt) {
|
|
+ /* Send the revoke receive buffer */
|
|
+ revoke_packet = &net_device->revoke_packet;
|
|
+ memset(revoke_packet, 0, sizeof(struct nvsp_message));
|
|
+
|
|
+ revoke_packet->hdr.msg_type =
|
|
+ NVSP_MSG1_TYPE_REVOKE_RECV_BUF;
|
|
+ revoke_packet->msg.v1_msg.
|
|
+ revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
|
|
+
|
|
+ ret = vmbus_sendpacket(net_device->dev->channel,
|
|
+ revoke_packet,
|
|
+ sizeof(struct nvsp_message),
|
|
+ (unsigned long)revoke_packet,
|
|
+ VM_PKT_DATA_INBAND, 0);
|
|
+ /*
|
|
+ * If we failed here, we might as well return and
|
|
+ * have a leak rather than continue and a bugchk
|
|
+ */
|
|
+ if (ret != 0) {
|
|
+ netdev_err(ndev, "unable to send "
|
|
+ "revoke receive buffer to netvsp\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Teardown the gpadl on the vsp end */
|
|
+ if (net_device->recv_buf_gpadl_handle) {
|
|
+ ret = vmbus_teardown_gpadl(net_device->dev->channel,
|
|
+ net_device->recv_buf_gpadl_handle);
|
|
+
|
|
+ /* If we failed here, we might as well return and have a leak
|
|
+ * rather than continue and a bugchk
|
|
+ */
|
|
+ if (ret != 0) {
|
|
+ netdev_err(ndev,
|
|
+ "unable to teardown receive buffer's gpadl\n");
|
|
+ return ret;
|
|
+ }
|
|
+ net_device->recv_buf_gpadl_handle = 0;
|
|
+ }
|
|
+
|
|
+ if (net_device->recv_buf) {
|
|
+ /* Free up the receive buffer */
|
|
+ free_pages((unsigned long)net_device->recv_buf,
|
|
+ get_order(net_device->recv_buf_size));
|
|
+ net_device->recv_buf = NULL;
|
|
+ }
|
|
+
|
|
+ if (net_device->recv_section) {
|
|
+ net_device->recv_section_cnt = 0;
|
|
+ kfree(net_device->recv_section);
|
|
+ net_device->recv_section = NULL;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int netvsc_init_recv_buf(struct hv_device *device)
|
|
+{
|
|
+ int ret = 0;
|
|
+ int t;
|
|
+ struct netvsc_device *net_device;
|
|
+ struct nvsp_message *init_packet;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ net_device = get_outbound_net_device(device);
|
|
+ if (!net_device)
|
|
+ return -ENODEV;
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ net_device->recv_buf =
|
|
+ (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
|
|
+ get_order(net_device->recv_buf_size));
|
|
+ if (!net_device->recv_buf) {
|
|
+ netdev_err(ndev, "unable to allocate receive "
|
|
+ "buffer of size %d\n", net_device->recv_buf_size);
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Establish the gpadl handle for this buffer on this
|
|
+ * channel. Note: This call uses the vmbus connection rather
|
|
+ * than the channel to establish the gpadl handle.
|
|
+ */
|
|
+ ret = vmbus_establish_gpadl(device->channel, net_device->recv_buf,
|
|
+ net_device->recv_buf_size,
|
|
+ &net_device->recv_buf_gpadl_handle);
|
|
+ if (ret != 0) {
|
|
+ netdev_err(ndev,
|
|
+ "unable to establish receive buffer's gpadl\n");
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+
|
|
+ /* Notify the NetVsp of the gpadl handle */
|
|
+ init_packet = &net_device->channel_init_pkt;
|
|
+
|
|
+ memset(init_packet, 0, sizeof(struct nvsp_message));
|
|
+
|
|
+ init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_RECV_BUF;
|
|
+ init_packet->msg.v1_msg.send_recv_buf.
|
|
+ gpadl_handle = net_device->recv_buf_gpadl_handle;
|
|
+ init_packet->msg.v1_msg.
|
|
+ send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
|
|
+
|
|
+ /* Send the gpadl notification request */
|
|
+ ret = vmbus_sendpacket(device->channel, init_packet,
|
|
+ sizeof(struct nvsp_message),
|
|
+ (unsigned long)init_packet,
|
|
+ VM_PKT_DATA_INBAND,
|
|
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
|
+ if (ret != 0) {
|
|
+ netdev_err(ndev,
|
|
+ "unable to send receive buffer's gpadl to netvsp\n");
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
|
|
+ BUG_ON(t == 0);
|
|
+
|
|
+
|
|
+ /* Check the response */
|
|
+ if (init_packet->msg.v1_msg.
|
|
+ send_recv_buf_complete.status != NVSP_STAT_SUCCESS) {
|
|
+ netdev_err(ndev, "Unable to complete receive buffer "
|
|
+ "initialization with NetVsp - status %d\n",
|
|
+ init_packet->msg.v1_msg.
|
|
+ send_recv_buf_complete.status);
|
|
+ ret = -EINVAL;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Parse the response */
|
|
+
|
|
+ net_device->recv_section_cnt = init_packet->msg.
|
|
+ v1_msg.send_recv_buf_complete.num_sections;
|
|
+
|
|
+ net_device->recv_section = kmemdup(init_packet->msg.v1_msg.send_recv_buf_complete.sections,
|
|
+ net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section),
|
|
+ GFP_KERNEL);
|
|
+ if (net_device->recv_section == NULL) {
|
|
+ ret = -EINVAL;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * For 1st release, there should only be 1 section that represents the
|
|
+ * entire receive buffer
|
|
+ */
|
|
+ if (net_device->recv_section_cnt != 1 ||
|
|
+ net_device->recv_section->offset != 0) {
|
|
+ ret = -EINVAL;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ goto exit;
|
|
+
|
|
+cleanup:
|
|
+ netvsc_destroy_recv_buf(net_device);
|
|
+
|
|
+exit:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static int netvsc_connect_vsp(struct hv_device *device)
|
|
+{
|
|
+ int ret, t;
|
|
+ struct netvsc_device *net_device;
|
|
+ struct nvsp_message *init_packet;
|
|
+ int ndis_version;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ net_device = get_outbound_net_device(device);
|
|
+ if (!net_device)
|
|
+ return -ENODEV;
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ init_packet = &net_device->channel_init_pkt;
|
|
+
|
|
+ memset(init_packet, 0, sizeof(struct nvsp_message));
|
|
+ init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT;
|
|
+ init_packet->msg.init_msg.init.min_protocol_ver =
|
|
+ NVSP_MIN_PROTOCOL_VERSION;
|
|
+ init_packet->msg.init_msg.init.max_protocol_ver =
|
|
+ NVSP_MAX_PROTOCOL_VERSION;
|
|
+
|
|
+ /* Send the init request */
|
|
+ ret = vmbus_sendpacket(device->channel, init_packet,
|
|
+ sizeof(struct nvsp_message),
|
|
+ (unsigned long)init_packet,
|
|
+ VM_PKT_DATA_INBAND,
|
|
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
|
+
|
|
+ if (ret != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
|
|
+
|
|
+ if (t == 0) {
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (init_packet->msg.init_msg.init_complete.status !=
|
|
+ NVSP_STAT_SUCCESS) {
|
|
+ ret = -EINVAL;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (init_packet->msg.init_msg.init_complete.
|
|
+ negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) {
|
|
+ ret = -EPROTO;
|
|
+ goto cleanup;
|
|
+ }
|
|
+ /* Send the ndis version */
|
|
+ memset(init_packet, 0, sizeof(struct nvsp_message));
|
|
+
|
|
+ ndis_version = 0x00050000;
|
|
+
|
|
+ init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER;
|
|
+ init_packet->msg.v1_msg.
|
|
+ send_ndis_ver.ndis_major_ver =
|
|
+ (ndis_version & 0xFFFF0000) >> 16;
|
|
+ init_packet->msg.v1_msg.
|
|
+ send_ndis_ver.ndis_minor_ver =
|
|
+ ndis_version & 0xFFFF;
|
|
+
|
|
+ /* Send the init request */
|
|
+ ret = vmbus_sendpacket(device->channel, init_packet,
|
|
+ sizeof(struct nvsp_message),
|
|
+ (unsigned long)init_packet,
|
|
+ VM_PKT_DATA_INBAND, 0);
|
|
+ if (ret != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ /* Post the big receive buffer to NetVSP */
|
|
+ ret = netvsc_init_recv_buf(device);
|
|
+
|
|
+cleanup:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
|
|
+{
|
|
+ netvsc_destroy_recv_buf(net_device);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * netvsc_device_remove - Callback when the root bus device is removed
|
|
+ */
|
|
+int netvsc_device_remove(struct hv_device *device)
|
|
+{
|
|
+ struct netvsc_device *net_device;
|
|
+ struct hv_netvsc_packet *netvsc_packet, *pos;
|
|
+ unsigned long flags;
|
|
+
|
|
+ net_device = hv_get_drvdata(device);
|
|
+ spin_lock_irqsave(&device->channel->inbound_lock, flags);
|
|
+ net_device->destroy = true;
|
|
+ spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
|
|
+
|
|
+ /* Wait for all send completions */
|
|
+ while (atomic_read(&net_device->num_outstanding_sends)) {
|
|
+ dev_info(&device->device,
|
|
+ "waiting for %d requests to complete...\n",
|
|
+ atomic_read(&net_device->num_outstanding_sends));
|
|
+ udelay(100);
|
|
+ }
|
|
+
|
|
+ netvsc_disconnect_vsp(net_device);
|
|
+
|
|
+ /*
|
|
+ * Since we have already drained, we don't need to busy wait
|
|
+ * as was done in final_release_stor_device()
|
|
+ * Note that we cannot set the ext pointer to NULL until
|
|
+ * we have drained - to drain the outgoing packets, we need to
|
|
+ * allow incoming packets.
|
|
+ */
|
|
+
|
|
+ spin_lock_irqsave(&device->channel->inbound_lock, flags);
|
|
+ hv_set_drvdata(device, NULL);
|
|
+ spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
|
|
+
|
|
+ /*
|
|
+ * At this point, no one should be accessing net_device
|
|
+ * except in here
|
|
+ */
|
|
+ dev_notice(&device->device, "net device safe to remove\n");
|
|
+
|
|
+ /* Now, we can close the channel safely */
|
|
+ vmbus_close(device->channel);
|
|
+
|
|
+ /* Release all resources */
|
|
+ list_for_each_entry_safe(netvsc_packet, pos,
|
|
+ &net_device->recv_pkt_list, list_ent) {
|
|
+ list_del(&netvsc_packet->list_ent);
|
|
+ kfree(netvsc_packet);
|
|
+ }
|
|
+
|
|
+ kfree(net_device);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void netvsc_send_completion(struct hv_device *device,
|
|
+ struct vmpacket_descriptor *packet)
|
|
+{
|
|
+ struct netvsc_device *net_device;
|
|
+ struct nvsp_message *nvsp_packet;
|
|
+ struct hv_netvsc_packet *nvsc_packet;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ net_device = get_inbound_net_device(device);
|
|
+ if (!net_device)
|
|
+ return;
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
|
|
+ (packet->offset8 << 3));
|
|
+
|
|
+ if ((nvsp_packet->hdr.msg_type == NVSP_MSG_TYPE_INIT_COMPLETE) ||
|
|
+ (nvsp_packet->hdr.msg_type ==
|
|
+ NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) ||
|
|
+ (nvsp_packet->hdr.msg_type ==
|
|
+ NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) {
|
|
+ /* Copy the response back */
|
|
+ memcpy(&net_device->channel_init_pkt, nvsp_packet,
|
|
+ sizeof(struct nvsp_message));
|
|
+ complete(&net_device->channel_init_wait);
|
|
+ } else if (nvsp_packet->hdr.msg_type ==
|
|
+ NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) {
|
|
+ /* Get the send context */
|
|
+ nvsc_packet = (struct hv_netvsc_packet *)(unsigned long)
|
|
+ packet->trans_id;
|
|
+
|
|
+ /* Notify the layer above us */
|
|
+ nvsc_packet->completion.send.send_completion(
|
|
+ nvsc_packet->completion.send.send_completion_ctx);
|
|
+
|
|
+ atomic_dec(&net_device->num_outstanding_sends);
|
|
+ } else {
|
|
+ netdev_err(ndev, "Unknown send completion packet type- "
|
|
+ "%d received!!\n", nvsp_packet->hdr.msg_type);
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+int netvsc_send(struct hv_device *device,
|
|
+ struct hv_netvsc_packet *packet)
|
|
+{
|
|
+ struct netvsc_device *net_device;
|
|
+ int ret = 0;
|
|
+ struct nvsp_message sendMessage;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ net_device = get_outbound_net_device(device);
|
|
+ if (!net_device)
|
|
+ return -ENODEV;
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
|
|
+ if (packet->is_data_pkt) {
|
|
+ /* 0 is RMC_DATA; */
|
|
+ sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 0;
|
|
+ } else {
|
|
+ /* 1 is RMC_CONTROL; */
|
|
+ sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1;
|
|
+ }
|
|
+
|
|
+ /* Not using send buffer section */
|
|
+ sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index =
|
|
+ 0xFFFFFFFF;
|
|
+ sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0;
|
|
+
|
|
+ if (packet->page_buf_cnt) {
|
|
+ ret = vmbus_sendpacket_pagebuffer(device->channel,
|
|
+ packet->page_buf,
|
|
+ packet->page_buf_cnt,
|
|
+ &sendMessage,
|
|
+ sizeof(struct nvsp_message),
|
|
+ (unsigned long)packet);
|
|
+ } else {
|
|
+ ret = vmbus_sendpacket(device->channel, &sendMessage,
|
|
+ sizeof(struct nvsp_message),
|
|
+ (unsigned long)packet,
|
|
+ VM_PKT_DATA_INBAND,
|
|
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
|
+
|
|
+ }
|
|
+
|
|
+ if (ret != 0)
|
|
+ netdev_err(ndev, "Unable to send packet %p ret %d\n",
|
|
+ packet, ret);
|
|
+ else
|
|
+ atomic_inc(&net_device->num_outstanding_sends);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void netvsc_send_recv_completion(struct hv_device *device,
|
|
+ u64 transaction_id)
|
|
+{
|
|
+ struct nvsp_message recvcompMessage;
|
|
+ int retries = 0;
|
|
+ int ret;
|
|
+ struct net_device *ndev;
|
|
+ struct netvsc_device *net_device = hv_get_drvdata(device);
|
|
+
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ recvcompMessage.hdr.msg_type =
|
|
+ NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE;
|
|
+
|
|
+ /* FIXME: Pass in the status */
|
|
+ recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status =
|
|
+ NVSP_STAT_SUCCESS;
|
|
+
|
|
+retry_send_cmplt:
|
|
+ /* Send the completion */
|
|
+ ret = vmbus_sendpacket(device->channel, &recvcompMessage,
|
|
+ sizeof(struct nvsp_message), transaction_id,
|
|
+ VM_PKT_COMP, 0);
|
|
+ if (ret == 0) {
|
|
+ /* success */
|
|
+ /* no-op */
|
|
+ } else if (ret == -EAGAIN) {
|
|
+ /* no more room...wait a bit and attempt to retry 3 times */
|
|
+ retries++;
|
|
+ netdev_err(ndev, "unable to send receive completion pkt"
|
|
+ " (tid %llx)...retrying %d\n", transaction_id, retries);
|
|
+
|
|
+ if (retries < 4) {
|
|
+ udelay(100);
|
|
+ goto retry_send_cmplt;
|
|
+ } else {
|
|
+ netdev_err(ndev, "unable to send receive "
|
|
+ "completion pkt (tid %llx)...give up retrying\n",
|
|
+ transaction_id);
|
|
+ }
|
|
+ } else {
|
|
+ netdev_err(ndev, "unable to send receive "
|
|
+ "completion pkt - %llx\n", transaction_id);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Send a receive completion packet to RNDIS device (ie NetVsp) */
|
|
+static void netvsc_receive_completion(void *context)
|
|
+{
|
|
+ struct hv_netvsc_packet *packet = context;
|
|
+ struct hv_device *device = (struct hv_device *)packet->device;
|
|
+ struct netvsc_device *net_device;
|
|
+ u64 transaction_id = 0;
|
|
+ bool fsend_receive_comp = false;
|
|
+ unsigned long flags;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ /*
|
|
+ * Even though it seems logical to do a GetOutboundNetDevice() here to
|
|
+ * send out receive completion, we are using GetInboundNetDevice()
|
|
+ * since we may have disable outbound traffic already.
|
|
+ */
|
|
+ net_device = get_inbound_net_device(device);
|
|
+ if (!net_device)
|
|
+ return;
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ /* Overloading use of the lock. */
|
|
+ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
|
|
+
|
|
+ packet->xfer_page_pkt->count--;
|
|
+
|
|
+ /*
|
|
+ * Last one in the line that represent 1 xfer page packet.
|
|
+ * Return the xfer page packet itself to the freelist
|
|
+ */
|
|
+ if (packet->xfer_page_pkt->count == 0) {
|
|
+ fsend_receive_comp = true;
|
|
+ transaction_id = packet->completion.recv.recv_completion_tid;
|
|
+ list_add_tail(&packet->xfer_page_pkt->list_ent,
|
|
+ &net_device->recv_pkt_list);
|
|
+
|
|
+ }
|
|
+
|
|
+ /* Put the packet back */
|
|
+ list_add_tail(&packet->list_ent, &net_device->recv_pkt_list);
|
|
+ spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
|
|
+
|
|
+ /* Send a receive completion for the xfer page packet */
|
|
+ if (fsend_receive_comp)
|
|
+ netvsc_send_recv_completion(device, transaction_id);
|
|
+
|
|
+}
|
|
+
|
|
+static void netvsc_receive(struct hv_device *device,
|
|
+ struct vmpacket_descriptor *packet)
|
|
+{
|
|
+ struct netvsc_device *net_device;
|
|
+ struct vmtransfer_page_packet_header *vmxferpage_packet;
|
|
+ struct nvsp_message *nvsp_packet;
|
|
+ struct hv_netvsc_packet *netvsc_packet = NULL;
|
|
+ unsigned long start;
|
|
+ unsigned long end, end_virtual;
|
|
+ /* struct netvsc_driver *netvscDriver; */
|
|
+ struct xferpage_packet *xferpage_packet = NULL;
|
|
+ int i, j;
|
|
+ int count = 0, bytes_remain = 0;
|
|
+ unsigned long flags;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ LIST_HEAD(listHead);
|
|
+
|
|
+ net_device = get_inbound_net_device(device);
|
|
+ if (!net_device)
|
|
+ return;
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ /*
|
|
+ * All inbound packets other than send completion should be xfer page
|
|
+ * packet
|
|
+ */
|
|
+ if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) {
|
|
+ netdev_err(ndev, "Unknown packet type received - %d\n",
|
|
+ packet->type);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
|
|
+ (packet->offset8 << 3));
|
|
+
|
|
+ /* Make sure this is a valid nvsp packet */
|
|
+ if (nvsp_packet->hdr.msg_type !=
|
|
+ NVSP_MSG1_TYPE_SEND_RNDIS_PKT) {
|
|
+ netdev_err(ndev, "Unknown nvsp packet type received-"
|
|
+ " %d\n", nvsp_packet->hdr.msg_type);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet;
|
|
+
|
|
+ if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) {
|
|
+ netdev_err(ndev, "Invalid xfer page set id - "
|
|
+ "expecting %x got %x\n", NETVSC_RECEIVE_BUFFER_ID,
|
|
+ vmxferpage_packet->xfer_pageset_id);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Grab free packets (range count + 1) to represent this xfer
|
|
+ * page packet. +1 to represent the xfer page packet itself.
|
|
+ * We grab it here so that we know exactly how many we can
|
|
+ * fulfil
|
|
+ */
|
|
+ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
|
|
+ while (!list_empty(&net_device->recv_pkt_list)) {
|
|
+ list_move_tail(net_device->recv_pkt_list.next, &listHead);
|
|
+ if (++count == vmxferpage_packet->range_cnt + 1)
|
|
+ break;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
|
|
+
|
|
+ /*
|
|
+ * We need at least 2 netvsc pkts (1 to represent the xfer
|
|
+ * page and at least 1 for the range) i.e. we can handled
|
|
+ * some of the xfer page packet ranges...
|
|
+ */
|
|
+ if (count < 2) {
|
|
+ netdev_err(ndev, "Got only %d netvsc pkt...needed "
|
|
+ "%d pkts. Dropping this xfer page packet completely!\n",
|
|
+ count, vmxferpage_packet->range_cnt + 1);
|
|
+
|
|
+ /* Return it to the freelist */
|
|
+ spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
|
|
+ for (i = count; i != 0; i--) {
|
|
+ list_move_tail(listHead.next,
|
|
+ &net_device->recv_pkt_list);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&net_device->recv_pkt_list_lock,
|
|
+ flags);
|
|
+
|
|
+ netvsc_send_recv_completion(device,
|
|
+ vmxferpage_packet->d.trans_id);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Remove the 1st packet to represent the xfer page packet itself */
|
|
+ xferpage_packet = (struct xferpage_packet *)listHead.next;
|
|
+ list_del(&xferpage_packet->list_ent);
|
|
+
|
|
+ /* This is how much we can satisfy */
|
|
+ xferpage_packet->count = count - 1;
|
|
+
|
|
+ if (xferpage_packet->count != vmxferpage_packet->range_cnt) {
|
|
+ netdev_err(ndev, "Needed %d netvsc pkts to satisfy "
|
|
+ "this xfer page...got %d\n",
|
|
+ vmxferpage_packet->range_cnt, xferpage_packet->count);
|
|
+ }
|
|
+
|
|
+ /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
|
|
+ for (i = 0; i < (count - 1); i++) {
|
|
+ netvsc_packet = (struct hv_netvsc_packet *)listHead.next;
|
|
+ list_del(&netvsc_packet->list_ent);
|
|
+
|
|
+ /* Initialize the netvsc packet */
|
|
+ netvsc_packet->xfer_page_pkt = xferpage_packet;
|
|
+ netvsc_packet->completion.recv.recv_completion =
|
|
+ netvsc_receive_completion;
|
|
+ netvsc_packet->completion.recv.recv_completion_ctx =
|
|
+ netvsc_packet;
|
|
+ netvsc_packet->device = device;
|
|
+ /* Save this so that we can send it back */
|
|
+ netvsc_packet->completion.recv.recv_completion_tid =
|
|
+ vmxferpage_packet->d.trans_id;
|
|
+
|
|
+ netvsc_packet->total_data_buflen =
|
|
+ vmxferpage_packet->ranges[i].byte_count;
|
|
+ netvsc_packet->page_buf_cnt = 1;
|
|
+
|
|
+ netvsc_packet->page_buf[0].len =
|
|
+ vmxferpage_packet->ranges[i].byte_count;
|
|
+
|
|
+ start = virt_to_phys((void *)((unsigned long)net_device->
|
|
+ recv_buf + vmxferpage_packet->ranges[i].byte_offset));
|
|
+
|
|
+ netvsc_packet->page_buf[0].pfn = start >> PAGE_SHIFT;
|
|
+ end_virtual = (unsigned long)net_device->recv_buf
|
|
+ + vmxferpage_packet->ranges[i].byte_offset
|
|
+ + vmxferpage_packet->ranges[i].byte_count - 1;
|
|
+ end = virt_to_phys((void *)end_virtual);
|
|
+
|
|
+ /* Calculate the page relative offset */
|
|
+ netvsc_packet->page_buf[0].offset =
|
|
+ vmxferpage_packet->ranges[i].byte_offset &
|
|
+ (PAGE_SIZE - 1);
|
|
+ if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) {
|
|
+ /* Handle frame across multiple pages: */
|
|
+ netvsc_packet->page_buf[0].len =
|
|
+ (netvsc_packet->page_buf[0].pfn <<
|
|
+ PAGE_SHIFT)
|
|
+ + PAGE_SIZE - start;
|
|
+ bytes_remain = netvsc_packet->total_data_buflen -
|
|
+ netvsc_packet->page_buf[0].len;
|
|
+ for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) {
|
|
+ netvsc_packet->page_buf[j].offset = 0;
|
|
+ if (bytes_remain <= PAGE_SIZE) {
|
|
+ netvsc_packet->page_buf[j].len =
|
|
+ bytes_remain;
|
|
+ bytes_remain = 0;
|
|
+ } else {
|
|
+ netvsc_packet->page_buf[j].len =
|
|
+ PAGE_SIZE;
|
|
+ bytes_remain -= PAGE_SIZE;
|
|
+ }
|
|
+ netvsc_packet->page_buf[j].pfn =
|
|
+ virt_to_phys((void *)(end_virtual -
|
|
+ bytes_remain)) >> PAGE_SHIFT;
|
|
+ netvsc_packet->page_buf_cnt++;
|
|
+ if (bytes_remain == 0)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Pass it to the upper layer */
|
|
+ rndis_filter_receive(device, netvsc_packet);
|
|
+
|
|
+ netvsc_receive_completion(netvsc_packet->
|
|
+ completion.recv.recv_completion_ctx);
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+static void netvsc_channel_cb(void *context)
|
|
+{
|
|
+ int ret;
|
|
+ struct hv_device *device = context;
|
|
+ struct netvsc_device *net_device;
|
|
+ u32 bytes_recvd;
|
|
+ u64 request_id;
|
|
+ unsigned char *packet;
|
|
+ struct vmpacket_descriptor *desc;
|
|
+ unsigned char *buffer;
|
|
+ int bufferlen = NETVSC_PACKET_SIZE;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char),
|
|
+ GFP_ATOMIC);
|
|
+ if (!packet)
|
|
+ return;
|
|
+ buffer = packet;
|
|
+
|
|
+ net_device = get_inbound_net_device(device);
|
|
+ if (!net_device)
|
|
+ goto out;
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ do {
|
|
+ ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen,
|
|
+ &bytes_recvd, &request_id);
|
|
+ if (ret == 0) {
|
|
+ if (bytes_recvd > 0) {
|
|
+ desc = (struct vmpacket_descriptor *)buffer;
|
|
+ switch (desc->type) {
|
|
+ case VM_PKT_COMP:
|
|
+ netvsc_send_completion(device, desc);
|
|
+ break;
|
|
+
|
|
+ case VM_PKT_DATA_USING_XFER_PAGES:
|
|
+ netvsc_receive(device, desc);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ netdev_err(ndev,
|
|
+ "unhandled packet type %d, "
|
|
+ "tid %llx len %d\n",
|
|
+ desc->type, request_id,
|
|
+ bytes_recvd);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* reset */
|
|
+ if (bufferlen > NETVSC_PACKET_SIZE) {
|
|
+ kfree(buffer);
|
|
+ buffer = packet;
|
|
+ bufferlen = NETVSC_PACKET_SIZE;
|
|
+ }
|
|
+ } else {
|
|
+ /* reset */
|
|
+ if (bufferlen > NETVSC_PACKET_SIZE) {
|
|
+ kfree(buffer);
|
|
+ buffer = packet;
|
|
+ bufferlen = NETVSC_PACKET_SIZE;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ } else if (ret == -ENOBUFS) {
|
|
+ /* Handle large packet */
|
|
+ buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
|
|
+ if (buffer == NULL) {
|
|
+ /* Try again next time around */
|
|
+ netdev_err(ndev,
|
|
+ "unable to allocate buffer of size "
|
|
+ "(%d)!!\n", bytes_recvd);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ bufferlen = bytes_recvd;
|
|
+ }
|
|
+ } while (1);
|
|
+
|
|
+out:
|
|
+ kfree(buffer);
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * netvsc_device_add - Callback when the device belonging to this
|
|
+ * driver is added
|
|
+ */
|
|
+int netvsc_device_add(struct hv_device *device, void *additional_info)
|
|
+{
|
|
+ int ret = 0;
|
|
+ int i;
|
|
+ int ring_size =
|
|
+ ((struct netvsc_device_info *)additional_info)->ring_size;
|
|
+ struct netvsc_device *net_device;
|
|
+ struct hv_netvsc_packet *packet, *pos;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ net_device = alloc_net_device(device);
|
|
+ if (!net_device) {
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Coming into this function, struct net_device * is
|
|
+ * registered as the driver private data.
|
|
+ * In alloc_net_device(), we register struct netvsc_device *
|
|
+ * as the driver private data and stash away struct net_device *
|
|
+ * in struct netvsc_device *.
|
|
+ */
|
|
+ ndev = net_device->ndev;
|
|
+
|
|
+ /* Initialize the NetVSC channel extension */
|
|
+ net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
|
|
+ spin_lock_init(&net_device->recv_pkt_list_lock);
|
|
+
|
|
+ INIT_LIST_HEAD(&net_device->recv_pkt_list);
|
|
+
|
|
+ for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) {
|
|
+ packet = kzalloc(sizeof(struct hv_netvsc_packet) +
|
|
+ (NETVSC_RECEIVE_SG_COUNT *
|
|
+ sizeof(struct hv_page_buffer)), GFP_KERNEL);
|
|
+ if (!packet)
|
|
+ break;
|
|
+
|
|
+ list_add_tail(&packet->list_ent,
|
|
+ &net_device->recv_pkt_list);
|
|
+ }
|
|
+ init_completion(&net_device->channel_init_wait);
|
|
+
|
|
+ /* Open the channel */
|
|
+ ret = vmbus_open(device->channel, ring_size * PAGE_SIZE,
|
|
+ ring_size * PAGE_SIZE, NULL, 0,
|
|
+ netvsc_channel_cb, device);
|
|
+
|
|
+ if (ret != 0) {
|
|
+ netdev_err(ndev, "unable to open channel: %d\n", ret);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Channel is opened */
|
|
+ pr_info("hv_netvsc channel opened successfully\n");
|
|
+
|
|
+ /* Connect with the NetVsp */
|
|
+ ret = netvsc_connect_vsp(device);
|
|
+ if (ret != 0) {
|
|
+ netdev_err(ndev,
|
|
+ "unable to connect to NetVSP - %d\n", ret);
|
|
+ goto close;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+
|
|
+close:
|
|
+ /* Now, we can close the channel safely */
|
|
+ vmbus_close(device->channel);
|
|
+
|
|
+cleanup:
|
|
+
|
|
+ if (net_device) {
|
|
+ list_for_each_entry_safe(packet, pos,
|
|
+ &net_device->recv_pkt_list,
|
|
+ list_ent) {
|
|
+ list_del(&packet->list_ent);
|
|
+ kfree(packet);
|
|
+ }
|
|
+
|
|
+ kfree(net_device);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
|
|
new file mode 100644
|
|
index 0000000..93b0e91
|
|
--- /dev/null
|
|
+++ b/drivers/net/hyperv/netvsc_drv.c
|
|
@@ -0,0 +1,456 @@
|
|
+/*
|
|
+ * Copyright (c) 2009, Microsoft Corporation.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms and conditions of the GNU General Public License,
|
|
+ * version 2, as published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Haiyang Zhang <haiyangz@microsoft.com>
|
|
+ * Hank Janssen <hjanssen@microsoft.com>
|
|
+ */
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/atomic.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/highmem.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/inetdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/in.h>
|
|
+#include <linux/slab.h>
|
|
+#include <net/arp.h>
|
|
+#include <net/route.h>
|
|
+#include <net/sock.h>
|
|
+#include <net/pkt_sched.h>
|
|
+
|
|
+#include "hyperv_net.h"
|
|
+
|
|
+struct net_device_context {
|
|
+ /* point back to our device context */
|
|
+ struct hv_device *device_ctx;
|
|
+ atomic_t avail;
|
|
+ struct delayed_work dwork;
|
|
+};
|
|
+
|
|
+
|
|
+#define PACKET_PAGES_LOWATER 8
|
|
+/* Need this many pages to handle worst case fragmented packet */
|
|
+#define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2)
|
|
+
|
|
+static int ring_size = 128;
|
|
+module_param(ring_size, int, S_IRUGO);
|
|
+MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
|
|
+
|
|
+/* no-op so the netdev core doesn't return -EINVAL when modifying the the
|
|
+ * multicast address list in SIOCADDMULTI. hv is setup to get all multicast
|
|
+ * when it calls RndisFilterOnOpen() */
|
|
+static void netvsc_set_multicast_list(struct net_device *net)
|
|
+{
|
|
+}
|
|
+
|
|
+static int netvsc_open(struct net_device *net)
|
|
+{
|
|
+ struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
+ struct hv_device *device_obj = net_device_ctx->device_ctx;
|
|
+ int ret = 0;
|
|
+
|
|
+ /* Open up the device */
|
|
+ ret = rndis_filter_open(device_obj);
|
|
+ if (ret != 0) {
|
|
+ netdev_err(net, "unable to open device (ret %d).\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ netif_start_queue(net);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int netvsc_close(struct net_device *net)
|
|
+{
|
|
+ struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
+ struct hv_device *device_obj = net_device_ctx->device_ctx;
|
|
+ int ret;
|
|
+
|
|
+ netif_stop_queue(net);
|
|
+
|
|
+ ret = rndis_filter_close(device_obj);
|
|
+ if (ret != 0)
|
|
+ netdev_err(net, "unable to close device (ret %d).\n", ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void netvsc_xmit_completion(void *context)
|
|
+{
|
|
+ struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
|
|
+ struct sk_buff *skb = (struct sk_buff *)
|
|
+ (unsigned long)packet->completion.send.send_completion_tid;
|
|
+
|
|
+ kfree(packet);
|
|
+
|
|
+ if (skb) {
|
|
+ struct net_device *net = skb->dev;
|
|
+ struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
+ unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2;
|
|
+
|
|
+ dev_kfree_skb_any(skb);
|
|
+
|
|
+ atomic_add(num_pages, &net_device_ctx->avail);
|
|
+ if (atomic_read(&net_device_ctx->avail) >=
|
|
+ PACKET_PAGES_HIWATER)
|
|
+ netif_wake_queue(net);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
|
+{
|
|
+ struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
+ struct hv_netvsc_packet *packet;
|
|
+ int ret;
|
|
+ unsigned int i, num_pages;
|
|
+
|
|
+ /* Add 1 for skb->data and additional one for RNDIS */
|
|
+ num_pages = skb_shinfo(skb)->nr_frags + 1 + 1;
|
|
+ if (num_pages > atomic_read(&net_device_ctx->avail))
|
|
+ return NETDEV_TX_BUSY;
|
|
+
|
|
+ /* Allocate a netvsc packet based on # of frags. */
|
|
+ packet = kzalloc(sizeof(struct hv_netvsc_packet) +
|
|
+ (num_pages * sizeof(struct hv_page_buffer)) +
|
|
+ sizeof(struct rndis_filter_packet), GFP_ATOMIC);
|
|
+ if (!packet) {
|
|
+ /* out of memory, drop packet */
|
|
+ netdev_err(net, "unable to allocate hv_netvsc_packet\n");
|
|
+
|
|
+ dev_kfree_skb(skb);
|
|
+ net->stats.tx_dropped++;
|
|
+ return NETDEV_TX_BUSY;
|
|
+ }
|
|
+
|
|
+ packet->extension = (void *)(unsigned long)packet +
|
|
+ sizeof(struct hv_netvsc_packet) +
|
|
+ (num_pages * sizeof(struct hv_page_buffer));
|
|
+
|
|
+ /* Setup the rndis header */
|
|
+ packet->page_buf_cnt = num_pages;
|
|
+
|
|
+ /* Initialize it from the skb */
|
|
+ packet->total_data_buflen = skb->len;
|
|
+
|
|
+ /* Start filling in the page buffers starting after RNDIS buffer. */
|
|
+ packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
|
|
+ packet->page_buf[1].offset
|
|
+ = (unsigned long)skb->data & (PAGE_SIZE - 1);
|
|
+ packet->page_buf[1].len = skb_headlen(skb);
|
|
+
|
|
+ /* Additional fragments are after SKB data */
|
|
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
|
+ const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
|
|
+
|
|
+ packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f));
|
|
+ packet->page_buf[i+2].offset = f->page_offset;
|
|
+ packet->page_buf[i+2].len = skb_frag_size(f);
|
|
+ }
|
|
+
|
|
+ /* Set the completion routine */
|
|
+ packet->completion.send.send_completion = netvsc_xmit_completion;
|
|
+ packet->completion.send.send_completion_ctx = packet;
|
|
+ packet->completion.send.send_completion_tid = (unsigned long)skb;
|
|
+
|
|
+ ret = rndis_filter_send(net_device_ctx->device_ctx,
|
|
+ packet);
|
|
+ if (ret == 0) {
|
|
+ net->stats.tx_bytes += skb->len;
|
|
+ net->stats.tx_packets++;
|
|
+
|
|
+ atomic_sub(num_pages, &net_device_ctx->avail);
|
|
+ if (atomic_read(&net_device_ctx->avail) < PACKET_PAGES_LOWATER)
|
|
+ netif_stop_queue(net);
|
|
+ } else {
|
|
+ /* we are shutting down or bus overloaded, just drop packet */
|
|
+ net->stats.tx_dropped++;
|
|
+ kfree(packet);
|
|
+ dev_kfree_skb_any(skb);
|
|
+ }
|
|
+
|
|
+ return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * netvsc_linkstatus_callback - Link up/down notification
|
|
+ */
|
|
+void netvsc_linkstatus_callback(struct hv_device *device_obj,
|
|
+ unsigned int status)
|
|
+{
|
|
+ struct net_device *net;
|
|
+ struct net_device_context *ndev_ctx;
|
|
+ struct netvsc_device *net_device;
|
|
+
|
|
+ net_device = hv_get_drvdata(device_obj);
|
|
+ net = net_device->ndev;
|
|
+
|
|
+ if (!net) {
|
|
+ netdev_err(net, "got link status but net device "
|
|
+ "not initialized yet\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (status == 1) {
|
|
+ netif_carrier_on(net);
|
|
+ netif_wake_queue(net);
|
|
+ ndev_ctx = netdev_priv(net);
|
|
+ schedule_delayed_work(&ndev_ctx->dwork, 0);
|
|
+ schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));
|
|
+ } else {
|
|
+ netif_carrier_off(net);
|
|
+ netif_stop_queue(net);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * netvsc_recv_callback - Callback when we receive a packet from the
|
|
+ * "wire" on the specified device.
|
|
+ */
|
|
+int netvsc_recv_callback(struct hv_device *device_obj,
|
|
+ struct hv_netvsc_packet *packet)
|
|
+{
|
|
+ struct net_device *net = dev_get_drvdata(&device_obj->device);
|
|
+ struct sk_buff *skb;
|
|
+ void *data;
|
|
+ int i;
|
|
+ unsigned long flags;
|
|
+ struct netvsc_device *net_device;
|
|
+
|
|
+ net_device = hv_get_drvdata(device_obj);
|
|
+ net = net_device->ndev;
|
|
+
|
|
+ if (!net) {
|
|
+ netdev_err(net, "got receive callback but net device"
|
|
+ " not initialized yet\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Allocate a skb - TODO direct I/O to pages? */
|
|
+ skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen);
|
|
+ if (unlikely(!skb)) {
|
|
+ ++net->stats.rx_dropped;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* for kmap_atomic */
|
|
+ local_irq_save(flags);
|
|
+
|
|
+ /*
|
|
+ * Copy to skb. This copy is needed here since the memory pointed by
|
|
+ * hv_netvsc_packet cannot be deallocated
|
|
+ */
|
|
+ for (i = 0; i < packet->page_buf_cnt; i++) {
|
|
+ data = kmap_atomic(pfn_to_page(packet->page_buf[i].pfn),
|
|
+ KM_IRQ1);
|
|
+ data = (void *)(unsigned long)data +
|
|
+ packet->page_buf[i].offset;
|
|
+
|
|
+ memcpy(skb_put(skb, packet->page_buf[i].len), data,
|
|
+ packet->page_buf[i].len);
|
|
+
|
|
+ kunmap_atomic((void *)((unsigned long)data -
|
|
+ packet->page_buf[i].offset), KM_IRQ1);
|
|
+ }
|
|
+
|
|
+ local_irq_restore(flags);
|
|
+
|
|
+ skb->protocol = eth_type_trans(skb, net);
|
|
+ skb->ip_summed = CHECKSUM_NONE;
|
|
+
|
|
+ net->stats.rx_packets++;
|
|
+ net->stats.rx_bytes += skb->len;
|
|
+
|
|
+ /*
|
|
+ * Pass the skb back up. Network stack will deallocate the skb when it
|
|
+ * is done.
|
|
+ * TODO - use NAPI?
|
|
+ */
|
|
+ netif_rx(skb);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void netvsc_get_drvinfo(struct net_device *net,
|
|
+ struct ethtool_drvinfo *info)
|
|
+{
|
|
+ strcpy(info->driver, "hv_netvsc");
|
|
+ strcpy(info->version, HV_DRV_VERSION);
|
|
+ strcpy(info->fw_version, "N/A");
|
|
+}
|
|
+
|
|
+static const struct ethtool_ops ethtool_ops = {
|
|
+ .get_drvinfo = netvsc_get_drvinfo,
|
|
+ .get_link = ethtool_op_get_link,
|
|
+};
|
|
+
|
|
+static const struct net_device_ops device_ops = {
|
|
+ .ndo_open = netvsc_open,
|
|
+ .ndo_stop = netvsc_close,
|
|
+ .ndo_start_xmit = netvsc_start_xmit,
|
|
+ .ndo_set_rx_mode = netvsc_set_multicast_list,
|
|
+ .ndo_change_mtu = eth_change_mtu,
|
|
+ .ndo_validate_addr = eth_validate_addr,
|
|
+ .ndo_set_mac_address = eth_mac_addr,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Send GARP packet to network peers after migrations.
|
|
+ * After Quick Migration, the network is not immediately operational in the
|
|
+ * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add
|
|
+ * another netif_notify_peers() into a delayed work, otherwise GARP packet
|
|
+ * will not be sent after quick migration, and cause network disconnection.
|
|
+ */
|
|
+static void netvsc_send_garp(struct work_struct *w)
|
|
+{
|
|
+ struct net_device_context *ndev_ctx;
|
|
+ struct net_device *net;
|
|
+ struct netvsc_device *net_device;
|
|
+
|
|
+ ndev_ctx = container_of(w, struct net_device_context, dwork.work);
|
|
+ net_device = hv_get_drvdata(ndev_ctx->device_ctx);
|
|
+ net = net_device->ndev;
|
|
+ netif_notify_peers(net);
|
|
+}
|
|
+
|
|
+
|
|
+static int netvsc_probe(struct hv_device *dev,
|
|
+ const struct hv_vmbus_device_id *dev_id)
|
|
+{
|
|
+ struct net_device *net = NULL;
|
|
+ struct net_device_context *net_device_ctx;
|
|
+ struct netvsc_device_info device_info;
|
|
+ int ret;
|
|
+
|
|
+ net = alloc_etherdev(sizeof(struct net_device_context));
|
|
+ if (!net)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Set initial state */
|
|
+ netif_carrier_off(net);
|
|
+
|
|
+ net_device_ctx = netdev_priv(net);
|
|
+ net_device_ctx->device_ctx = dev;
|
|
+ atomic_set(&net_device_ctx->avail, ring_size);
|
|
+ hv_set_drvdata(dev, net);
|
|
+ INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp);
|
|
+
|
|
+ net->netdev_ops = &device_ops;
|
|
+
|
|
+ /* TODO: Add GSO and Checksum offload */
|
|
+ net->hw_features = NETIF_F_SG;
|
|
+ net->features = NETIF_F_SG;
|
|
+
|
|
+ SET_ETHTOOL_OPS(net, ðtool_ops);
|
|
+ SET_NETDEV_DEV(net, &dev->device);
|
|
+
|
|
+ ret = register_netdev(net);
|
|
+ if (ret != 0) {
|
|
+ pr_err("Unable to register netdev.\n");
|
|
+ free_netdev(net);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Notify the netvsc driver of the new device */
|
|
+ device_info.ring_size = ring_size;
|
|
+ ret = rndis_filter_device_add(dev, &device_info);
|
|
+ if (ret != 0) {
|
|
+ netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
|
|
+ unregister_netdev(net);
|
|
+ free_netdev(net);
|
|
+ hv_set_drvdata(dev, NULL);
|
|
+ return ret;
|
|
+ }
|
|
+ memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
|
|
+
|
|
+ netif_carrier_on(net);
|
|
+
|
|
+out:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int netvsc_remove(struct hv_device *dev)
|
|
+{
|
|
+ struct net_device *net;
|
|
+ struct net_device_context *ndev_ctx;
|
|
+ struct netvsc_device *net_device;
|
|
+
|
|
+ net_device = hv_get_drvdata(dev);
|
|
+ net = net_device->ndev;
|
|
+
|
|
+ if (net == NULL) {
|
|
+ dev_err(&dev->device, "No net device to remove\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ndev_ctx = netdev_priv(net);
|
|
+ cancel_delayed_work_sync(&ndev_ctx->dwork);
|
|
+
|
|
+ /* Stop outbound asap */
|
|
+ netif_stop_queue(net);
|
|
+
|
|
+ unregister_netdev(net);
|
|
+
|
|
+ /*
|
|
+ * Call to the vsc driver to let it know that the device is being
|
|
+ * removed
|
|
+ */
|
|
+ rndis_filter_device_remove(dev);
|
|
+
|
|
+ free_netdev(net);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct hv_vmbus_device_id id_table[] = {
|
|
+ /* Network guid */
|
|
+ { VMBUS_DEVICE(0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
|
|
+ 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E) },
|
|
+ { },
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(vmbus, id_table);
|
|
+
|
|
+/* The one and only one */
|
|
+static struct hv_driver netvsc_drv = {
|
|
+ .name = "netvsc",
|
|
+ .id_table = id_table,
|
|
+ .probe = netvsc_probe,
|
|
+ .remove = netvsc_remove,
|
|
+};
|
|
+
|
|
+static void __exit netvsc_drv_exit(void)
|
|
+{
|
|
+ vmbus_driver_unregister(&netvsc_drv);
|
|
+}
|
|
+
|
|
+static int __init netvsc_drv_init(void)
|
|
+{
|
|
+ return vmbus_driver_register(&netvsc_drv);
|
|
+}
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_VERSION(HV_DRV_VERSION);
|
|
+MODULE_DESCRIPTION("Microsoft Hyper-V network driver");
|
|
+
|
|
+module_init(netvsc_drv_init);
|
|
+module_exit(netvsc_drv_exit);
|
|
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
|
|
new file mode 100644
|
|
index 0000000..bafccb3
|
|
--- /dev/null
|
|
+++ b/drivers/net/hyperv/rndis_filter.c
|
|
@@ -0,0 +1,855 @@
|
|
+/*
|
|
+ * Copyright (c) 2009, Microsoft Corporation.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms and conditions of the GNU General Public License,
|
|
+ * version 2, as published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ * more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along with
|
|
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
+ *
|
|
+ * Authors:
|
|
+ * Haiyang Zhang <haiyangz@microsoft.com>
|
|
+ * Hank Janssen <hjanssen@microsoft.com>
|
|
+ */
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/highmem.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/if_ether.h>
|
|
+#include <linux/netdevice.h>
|
|
+
|
|
+#include "hyperv_net.h"
|
|
+
|
|
+
|
|
+enum rndis_device_state {
|
|
+ RNDIS_DEV_UNINITIALIZED = 0,
|
|
+ RNDIS_DEV_INITIALIZING,
|
|
+ RNDIS_DEV_INITIALIZED,
|
|
+ RNDIS_DEV_DATAINITIALIZED,
|
|
+};
|
|
+
|
|
+struct rndis_device {
|
|
+ struct netvsc_device *net_dev;
|
|
+
|
|
+ enum rndis_device_state state;
|
|
+ bool link_state;
|
|
+ atomic_t new_req_id;
|
|
+
|
|
+ spinlock_t request_lock;
|
|
+ struct list_head req_list;
|
|
+
|
|
+ unsigned char hw_mac_adr[ETH_ALEN];
|
|
+};
|
|
+
|
|
+struct rndis_request {
|
|
+ struct list_head list_ent;
|
|
+ struct completion wait_event;
|
|
+
|
|
+ /*
|
|
+ * FIXME: We assumed a fixed size response here. If we do ever need to
|
|
+ * handle a bigger response, we can either define a max response
|
|
+ * message or add a response buffer variable above this field
|
|
+ */
|
|
+ struct rndis_message response_msg;
|
|
+
|
|
+ /* Simplify allocation by having a netvsc packet inline */
|
|
+ struct hv_netvsc_packet pkt;
|
|
+ struct hv_page_buffer buf;
|
|
+ /* FIXME: We assumed a fixed size request here. */
|
|
+ struct rndis_message request_msg;
|
|
+};
|
|
+
|
|
+static void rndis_filter_send_completion(void *ctx);
|
|
+
|
|
+static void rndis_filter_send_request_completion(void *ctx);
|
|
+
|
|
+
|
|
+
|
|
+static struct rndis_device *get_rndis_device(void)
|
|
+{
|
|
+ struct rndis_device *device;
|
|
+
|
|
+ device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
|
|
+ if (!device)
|
|
+ return NULL;
|
|
+
|
|
+ spin_lock_init(&device->request_lock);
|
|
+
|
|
+ INIT_LIST_HEAD(&device->req_list);
|
|
+
|
|
+ device->state = RNDIS_DEV_UNINITIALIZED;
|
|
+
|
|
+ return device;
|
|
+}
|
|
+
|
|
+static struct rndis_request *get_rndis_request(struct rndis_device *dev,
|
|
+ u32 msg_type,
|
|
+ u32 msg_len)
|
|
+{
|
|
+ struct rndis_request *request;
|
|
+ struct rndis_message *rndis_msg;
|
|
+ struct rndis_set_request *set;
|
|
+ unsigned long flags;
|
|
+
|
|
+ request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
|
|
+ if (!request)
|
|
+ return NULL;
|
|
+
|
|
+ init_completion(&request->wait_event);
|
|
+
|
|
+ rndis_msg = &request->request_msg;
|
|
+ rndis_msg->ndis_msg_type = msg_type;
|
|
+ rndis_msg->msg_len = msg_len;
|
|
+
|
|
+ /*
|
|
+ * Set the request id. This field is always after the rndis header for
|
|
+ * request/response packet types so we just used the SetRequest as a
|
|
+ * template
|
|
+ */
|
|
+ set = &rndis_msg->msg.set_req;
|
|
+ set->req_id = atomic_inc_return(&dev->new_req_id);
|
|
+
|
|
+ /* Add to the request list */
|
|
+ spin_lock_irqsave(&dev->request_lock, flags);
|
|
+ list_add_tail(&request->list_ent, &dev->req_list);
|
|
+ spin_unlock_irqrestore(&dev->request_lock, flags);
|
|
+
|
|
+ return request;
|
|
+}
|
|
+
|
|
+static void put_rndis_request(struct rndis_device *dev,
|
|
+ struct rndis_request *req)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&dev->request_lock, flags);
|
|
+ list_del(&req->list_ent);
|
|
+ spin_unlock_irqrestore(&dev->request_lock, flags);
|
|
+
|
|
+ kfree(req);
|
|
+}
|
|
+
|
|
+static void dump_rndis_message(struct hv_device *hv_dev,
|
|
+ struct rndis_message *rndis_msg)
|
|
+{
|
|
+ struct net_device *netdev;
|
|
+ struct netvsc_device *net_device;
|
|
+
|
|
+ net_device = hv_get_drvdata(hv_dev);
|
|
+ netdev = net_device->ndev;
|
|
+
|
|
+ switch (rndis_msg->ndis_msg_type) {
|
|
+ case REMOTE_NDIS_PACKET_MSG:
|
|
+ netdev_dbg(netdev, "REMOTE_NDIS_PACKET_MSG (len %u, "
|
|
+ "data offset %u data len %u, # oob %u, "
|
|
+ "oob offset %u, oob len %u, pkt offset %u, "
|
|
+ "pkt len %u\n",
|
|
+ rndis_msg->msg_len,
|
|
+ rndis_msg->msg.pkt.data_offset,
|
|
+ rndis_msg->msg.pkt.data_len,
|
|
+ rndis_msg->msg.pkt.num_oob_data_elements,
|
|
+ rndis_msg->msg.pkt.oob_data_offset,
|
|
+ rndis_msg->msg.pkt.oob_data_len,
|
|
+ rndis_msg->msg.pkt.per_pkt_info_offset,
|
|
+ rndis_msg->msg.pkt.per_pkt_info_len);
|
|
+ break;
|
|
+
|
|
+ case REMOTE_NDIS_INITIALIZE_CMPLT:
|
|
+ netdev_dbg(netdev, "REMOTE_NDIS_INITIALIZE_CMPLT "
|
|
+ "(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
|
|
+ "device flags %d, max xfer size 0x%x, max pkts %u, "
|
|
+ "pkt aligned %u)\n",
|
|
+ rndis_msg->msg_len,
|
|
+ rndis_msg->msg.init_complete.req_id,
|
|
+ rndis_msg->msg.init_complete.status,
|
|
+ rndis_msg->msg.init_complete.major_ver,
|
|
+ rndis_msg->msg.init_complete.minor_ver,
|
|
+ rndis_msg->msg.init_complete.dev_flags,
|
|
+ rndis_msg->msg.init_complete.max_xfer_size,
|
|
+ rndis_msg->msg.init_complete.
|
|
+ max_pkt_per_msg,
|
|
+ rndis_msg->msg.init_complete.
|
|
+ pkt_alignment_factor);
|
|
+ break;
|
|
+
|
|
+ case REMOTE_NDIS_QUERY_CMPLT:
|
|
+ netdev_dbg(netdev, "REMOTE_NDIS_QUERY_CMPLT "
|
|
+ "(len %u, id 0x%x, status 0x%x, buf len %u, "
|
|
+ "buf offset %u)\n",
|
|
+ rndis_msg->msg_len,
|
|
+ rndis_msg->msg.query_complete.req_id,
|
|
+ rndis_msg->msg.query_complete.status,
|
|
+ rndis_msg->msg.query_complete.
|
|
+ info_buflen,
|
|
+ rndis_msg->msg.query_complete.
|
|
+ info_buf_offset);
|
|
+ break;
|
|
+
|
|
+ case REMOTE_NDIS_SET_CMPLT:
|
|
+ netdev_dbg(netdev,
|
|
+ "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)\n",
|
|
+ rndis_msg->msg_len,
|
|
+ rndis_msg->msg.set_complete.req_id,
|
|
+ rndis_msg->msg.set_complete.status);
|
|
+ break;
|
|
+
|
|
+ case REMOTE_NDIS_INDICATE_STATUS_MSG:
|
|
+ netdev_dbg(netdev, "REMOTE_NDIS_INDICATE_STATUS_MSG "
|
|
+ "(len %u, status 0x%x, buf len %u, buf offset %u)\n",
|
|
+ rndis_msg->msg_len,
|
|
+ rndis_msg->msg.indicate_status.status,
|
|
+ rndis_msg->msg.indicate_status.status_buflen,
|
|
+ rndis_msg->msg.indicate_status.status_buf_offset);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ netdev_dbg(netdev, "0x%x (len %u)\n",
|
|
+ rndis_msg->ndis_msg_type,
|
|
+ rndis_msg->msg_len);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int rndis_filter_send_request(struct rndis_device *dev,
|
|
+ struct rndis_request *req)
|
|
+{
|
|
+ int ret;
|
|
+ struct hv_netvsc_packet *packet;
|
|
+
|
|
+ /* Setup the packet to send it */
|
|
+ packet = &req->pkt;
|
|
+
|
|
+ packet->is_data_pkt = false;
|
|
+ packet->total_data_buflen = req->request_msg.msg_len;
|
|
+ packet->page_buf_cnt = 1;
|
|
+
|
|
+ packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >>
|
|
+ PAGE_SHIFT;
|
|
+ packet->page_buf[0].len = req->request_msg.msg_len;
|
|
+ packet->page_buf[0].offset =
|
|
+ (unsigned long)&req->request_msg & (PAGE_SIZE - 1);
|
|
+
|
|
+ packet->completion.send.send_completion_ctx = req;/* packet; */
|
|
+ packet->completion.send.send_completion =
|
|
+ rndis_filter_send_request_completion;
|
|
+ packet->completion.send.send_completion_tid = (unsigned long)dev;
|
|
+
|
|
+ ret = netvsc_send(dev->net_dev->dev, packet);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void rndis_filter_receive_response(struct rndis_device *dev,
|
|
+ struct rndis_message *resp)
|
|
+{
|
|
+ struct rndis_request *request = NULL;
|
|
+ bool found = false;
|
|
+ unsigned long flags;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ ndev = dev->net_dev->ndev;
|
|
+
|
|
+ spin_lock_irqsave(&dev->request_lock, flags);
|
|
+ list_for_each_entry(request, &dev->req_list, list_ent) {
|
|
+ /*
|
|
+ * All request/response message contains RequestId as the 1st
|
|
+ * field
|
|
+ */
|
|
+ if (request->request_msg.msg.init_req.req_id
|
|
+ == resp->msg.init_complete.req_id) {
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ spin_unlock_irqrestore(&dev->request_lock, flags);
|
|
+
|
|
+ if (found) {
|
|
+ if (resp->msg_len <= sizeof(struct rndis_message)) {
|
|
+ memcpy(&request->response_msg, resp,
|
|
+ resp->msg_len);
|
|
+ } else {
|
|
+ netdev_err(ndev,
|
|
+ "rndis response buffer overflow "
|
|
+ "detected (size %u max %zu)\n",
|
|
+ resp->msg_len,
|
|
+ sizeof(struct rndis_filter_packet));
|
|
+
|
|
+ if (resp->ndis_msg_type ==
|
|
+ REMOTE_NDIS_RESET_CMPLT) {
|
|
+ /* does not have a request id field */
|
|
+ request->response_msg.msg.reset_complete.
|
|
+ status = STATUS_BUFFER_OVERFLOW;
|
|
+ } else {
|
|
+ request->response_msg.msg.
|
|
+ init_complete.status =
|
|
+ STATUS_BUFFER_OVERFLOW;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ complete(&request->wait_event);
|
|
+ } else {
|
|
+ netdev_err(ndev,
|
|
+ "no rndis request found for this response "
|
|
+ "(id 0x%x res type 0x%x)\n",
|
|
+ resp->msg.init_complete.req_id,
|
|
+ resp->ndis_msg_type);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void rndis_filter_receive_indicate_status(struct rndis_device *dev,
|
|
+ struct rndis_message *resp)
|
|
+{
|
|
+ struct rndis_indicate_status *indicate =
|
|
+ &resp->msg.indicate_status;
|
|
+
|
|
+ if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) {
|
|
+ netvsc_linkstatus_callback(
|
|
+ dev->net_dev->dev, 1);
|
|
+ } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) {
|
|
+ netvsc_linkstatus_callback(
|
|
+ dev->net_dev->dev, 0);
|
|
+ } else {
|
|
+ /*
|
|
+ * TODO:
|
|
+ */
|
|
+ }
|
|
+}
|
|
+
|
|
+static void rndis_filter_receive_data(struct rndis_device *dev,
|
|
+ struct rndis_message *msg,
|
|
+ struct hv_netvsc_packet *pkt)
|
|
+{
|
|
+ struct rndis_packet *rndis_pkt;
|
|
+ u32 data_offset;
|
|
+ int i;
|
|
+
|
|
+ rndis_pkt = &msg->msg.pkt;
|
|
+
|
|
+ /*
|
|
+ * FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this
|
|
+ * netvsc packet (ie TotalDataBufferLength != MessageLength)
|
|
+ */
|
|
+
|
|
+ /* Remove the rndis header and pass it back up the stack */
|
|
+ data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
|
|
+
|
|
+ pkt->total_data_buflen -= data_offset;
|
|
+ pkt->page_buf[0].offset += data_offset;
|
|
+ pkt->page_buf[0].len -= data_offset;
|
|
+
|
|
+ /* Drop the 0th page, if rndis data go beyond page boundary */
|
|
+ if (pkt->page_buf[0].offset >= PAGE_SIZE) {
|
|
+ pkt->page_buf[1].offset = pkt->page_buf[0].offset - PAGE_SIZE;
|
|
+ pkt->page_buf[1].len -= pkt->page_buf[1].offset;
|
|
+ pkt->page_buf_cnt--;
|
|
+ for (i = 0; i < pkt->page_buf_cnt; i++)
|
|
+ pkt->page_buf[i] = pkt->page_buf[i+1];
|
|
+ }
|
|
+
|
|
+ pkt->is_data_pkt = true;
|
|
+
|
|
+ netvsc_recv_callback(dev->net_dev->dev, pkt);
|
|
+}
|
|
+
|
|
+int rndis_filter_receive(struct hv_device *dev,
|
|
+ struct hv_netvsc_packet *pkt)
|
|
+{
|
|
+ struct netvsc_device *net_dev = hv_get_drvdata(dev);
|
|
+ struct rndis_device *rndis_dev;
|
|
+ struct rndis_message rndis_msg;
|
|
+ struct rndis_message *rndis_hdr;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ if (!net_dev)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ndev = net_dev->ndev;
|
|
+
|
|
+ /* Make sure the rndis device state is initialized */
|
|
+ if (!net_dev->extension) {
|
|
+ netdev_err(ndev, "got rndis message but no rndis device - "
|
|
+ "dropping this message!\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ rndis_dev = (struct rndis_device *)net_dev->extension;
|
|
+ if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
|
|
+ netdev_err(ndev, "got rndis message but rndis device "
|
|
+ "uninitialized...dropping this message!\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ rndis_hdr = (struct rndis_message *)kmap_atomic(
|
|
+ pfn_to_page(pkt->page_buf[0].pfn), KM_IRQ0);
|
|
+
|
|
+ rndis_hdr = (void *)((unsigned long)rndis_hdr +
|
|
+ pkt->page_buf[0].offset);
|
|
+
|
|
+ /* Make sure we got a valid rndis message */
|
|
+ if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) &&
|
|
+ (rndis_hdr->msg_len > sizeof(struct rndis_message))) {
|
|
+ netdev_err(ndev, "incoming rndis message buffer overflow "
|
|
+ "detected (got %u, max %zu)..marking it an error!\n",
|
|
+ rndis_hdr->msg_len,
|
|
+ sizeof(struct rndis_message));
|
|
+ }
|
|
+
|
|
+ memcpy(&rndis_msg, rndis_hdr,
|
|
+ (rndis_hdr->msg_len > sizeof(struct rndis_message)) ?
|
|
+ sizeof(struct rndis_message) :
|
|
+ rndis_hdr->msg_len);
|
|
+
|
|
+ kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0);
|
|
+
|
|
+ dump_rndis_message(dev, &rndis_msg);
|
|
+
|
|
+ switch (rndis_msg.ndis_msg_type) {
|
|
+ case REMOTE_NDIS_PACKET_MSG:
|
|
+ /* data msg */
|
|
+ rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt);
|
|
+ break;
|
|
+
|
|
+ case REMOTE_NDIS_INITIALIZE_CMPLT:
|
|
+ case REMOTE_NDIS_QUERY_CMPLT:
|
|
+ case REMOTE_NDIS_SET_CMPLT:
|
|
+ /* completion msgs */
|
|
+ rndis_filter_receive_response(rndis_dev, &rndis_msg);
|
|
+ break;
|
|
+
|
|
+ case REMOTE_NDIS_INDICATE_STATUS_MSG:
|
|
+ /* notification msgs */
|
|
+ rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg);
|
|
+ break;
|
|
+ default:
|
|
+ netdev_err(ndev,
|
|
+ "unhandled rndis message (type %u len %u)\n",
|
|
+ rndis_msg.ndis_msg_type,
|
|
+ rndis_msg.msg_len);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
|
|
+ void *result, u32 *result_size)
|
|
+{
|
|
+ struct rndis_request *request;
|
|
+ u32 inresult_size = *result_size;
|
|
+ struct rndis_query_request *query;
|
|
+ struct rndis_query_complete *query_complete;
|
|
+ int ret = 0;
|
|
+ int t;
|
|
+
|
|
+ if (!result)
|
|
+ return -EINVAL;
|
|
+
|
|
+ *result_size = 0;
|
|
+ request = get_rndis_request(dev, REMOTE_NDIS_QUERY_MSG,
|
|
+ RNDIS_MESSAGE_SIZE(struct rndis_query_request));
|
|
+ if (!request) {
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Setup the rndis query */
|
|
+ query = &request->request_msg.msg.query_req;
|
|
+ query->oid = oid;
|
|
+ query->info_buf_offset = sizeof(struct rndis_query_request);
|
|
+ query->info_buflen = 0;
|
|
+ query->dev_vc_handle = 0;
|
|
+
|
|
+ ret = rndis_filter_send_request(dev, request);
|
|
+ if (ret != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
|
|
+ if (t == 0) {
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Copy the response back */
|
|
+ query_complete = &request->response_msg.msg.query_complete;
|
|
+
|
|
+ if (query_complete->info_buflen > inresult_size) {
|
|
+ ret = -1;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ memcpy(result,
|
|
+ (void *)((unsigned long)query_complete +
|
|
+ query_complete->info_buf_offset),
|
|
+ query_complete->info_buflen);
|
|
+
|
|
+ *result_size = query_complete->info_buflen;
|
|
+
|
|
+cleanup:
|
|
+ if (request)
|
|
+ put_rndis_request(dev, request);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rndis_filter_query_device_mac(struct rndis_device *dev)
|
|
+{
|
|
+ u32 size = ETH_ALEN;
|
|
+
|
|
+ return rndis_filter_query_device(dev,
|
|
+ RNDIS_OID_802_3_PERMANENT_ADDRESS,
|
|
+ dev->hw_mac_adr, &size);
|
|
+}
|
|
+
|
|
+static int rndis_filter_query_device_link_status(struct rndis_device *dev)
|
|
+{
|
|
+ u32 size = sizeof(u32);
|
|
+ u32 link_status;
|
|
+ int ret;
|
|
+
|
|
+ ret = rndis_filter_query_device(dev,
|
|
+ RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
|
|
+ &link_status, &size);
|
|
+ dev->link_state = (link_status != 0) ? true : false;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rndis_filter_set_packet_filter(struct rndis_device *dev,
|
|
+ u32 new_filter)
|
|
+{
|
|
+ struct rndis_request *request;
|
|
+ struct rndis_set_request *set;
|
|
+ struct rndis_set_complete *set_complete;
|
|
+ u32 status;
|
|
+ int ret, t;
|
|
+ struct net_device *ndev;
|
|
+
|
|
+ ndev = dev->net_dev->ndev;
|
|
+
|
|
+ request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG,
|
|
+ RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
|
|
+ sizeof(u32));
|
|
+ if (!request) {
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Setup the rndis set */
|
|
+ set = &request->request_msg.msg.set_req;
|
|
+ set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
|
|
+ set->info_buflen = sizeof(u32);
|
|
+ set->info_buf_offset = sizeof(struct rndis_set_request);
|
|
+
|
|
+ memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
|
|
+ &new_filter, sizeof(u32));
|
|
+
|
|
+ ret = rndis_filter_send_request(dev, request);
|
|
+ if (ret != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
|
|
+
|
|
+ if (t == 0) {
|
|
+ netdev_err(ndev,
|
|
+ "timeout before we got a set response...\n");
|
|
+ /*
|
|
+ * We can't deallocate the request since we may still receive a
|
|
+ * send completion for it.
|
|
+ */
|
|
+ goto exit;
|
|
+ } else {
|
|
+ set_complete = &request->response_msg.msg.set_complete;
|
|
+ status = set_complete->status;
|
|
+ }
|
|
+
|
|
+cleanup:
|
|
+ if (request)
|
|
+ put_rndis_request(dev, request);
|
|
+exit:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static int rndis_filter_init_device(struct rndis_device *dev)
|
|
+{
|
|
+ struct rndis_request *request;
|
|
+ struct rndis_initialize_request *init;
|
|
+ struct rndis_initialize_complete *init_complete;
|
|
+ u32 status;
|
|
+ int ret, t;
|
|
+
|
|
+ request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG,
|
|
+ RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
|
|
+ if (!request) {
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Setup the rndis set */
|
|
+ init = &request->request_msg.msg.init_req;
|
|
+ init->major_ver = RNDIS_MAJOR_VERSION;
|
|
+ init->minor_ver = RNDIS_MINOR_VERSION;
|
|
+ /* FIXME: Use 1536 - rounded ethernet frame size */
|
|
+ init->max_xfer_size = 2048;
|
|
+
|
|
+ dev->state = RNDIS_DEV_INITIALIZING;
|
|
+
|
|
+ ret = rndis_filter_send_request(dev, request);
|
|
+ if (ret != 0) {
|
|
+ dev->state = RNDIS_DEV_UNINITIALIZED;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+
|
|
+ t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
|
|
+
|
|
+ if (t == 0) {
|
|
+ ret = -ETIMEDOUT;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ init_complete = &request->response_msg.msg.init_complete;
|
|
+ status = init_complete->status;
|
|
+ if (status == RNDIS_STATUS_SUCCESS) {
|
|
+ dev->state = RNDIS_DEV_INITIALIZED;
|
|
+ ret = 0;
|
|
+ } else {
|
|
+ dev->state = RNDIS_DEV_UNINITIALIZED;
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+
|
|
+cleanup:
|
|
+ if (request)
|
|
+ put_rndis_request(dev, request);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void rndis_filter_halt_device(struct rndis_device *dev)
|
|
+{
|
|
+ struct rndis_request *request;
|
|
+ struct rndis_halt_request *halt;
|
|
+
|
|
+ /* Attempt to do a rndis device halt */
|
|
+ request = get_rndis_request(dev, REMOTE_NDIS_HALT_MSG,
|
|
+ RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
|
|
+ if (!request)
|
|
+ goto cleanup;
|
|
+
|
|
+ /* Setup the rndis set */
|
|
+ halt = &request->request_msg.msg.halt_req;
|
|
+ halt->req_id = atomic_inc_return(&dev->new_req_id);
|
|
+
|
|
+ /* Ignore return since this msg is optional. */
|
|
+ rndis_filter_send_request(dev, request);
|
|
+
|
|
+ dev->state = RNDIS_DEV_UNINITIALIZED;
|
|
+
|
|
+cleanup:
|
|
+ if (request)
|
|
+ put_rndis_request(dev, request);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int rndis_filter_open_device(struct rndis_device *dev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (dev->state != RNDIS_DEV_INITIALIZED)
|
|
+ return 0;
|
|
+
|
|
+ ret = rndis_filter_set_packet_filter(dev,
|
|
+ NDIS_PACKET_TYPE_BROADCAST |
|
|
+ NDIS_PACKET_TYPE_ALL_MULTICAST |
|
|
+ NDIS_PACKET_TYPE_DIRECTED);
|
|
+ if (ret == 0)
|
|
+ dev->state = RNDIS_DEV_DATAINITIALIZED;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rndis_filter_close_device(struct rndis_device *dev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (dev->state != RNDIS_DEV_DATAINITIALIZED)
|
|
+ return 0;
|
|
+
|
|
+ ret = rndis_filter_set_packet_filter(dev, 0);
|
|
+ if (ret == 0)
|
|
+ dev->state = RNDIS_DEV_INITIALIZED;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int rndis_filter_device_add(struct hv_device *dev,
|
|
+ void *additional_info)
|
|
+{
|
|
+ int ret;
|
|
+ struct netvsc_device *net_device;
|
|
+ struct rndis_device *rndis_device;
|
|
+ struct netvsc_device_info *device_info = additional_info;
|
|
+
|
|
+ rndis_device = get_rndis_device();
|
|
+ if (!rndis_device)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /*
|
|
+ * Let the inner driver handle this first to create the netvsc channel
|
|
+ * NOTE! Once the channel is created, we may get a receive callback
|
|
+ * (RndisFilterOnReceive()) before this call is completed
|
|
+ */
|
|
+ ret = netvsc_device_add(dev, additional_info);
|
|
+ if (ret != 0) {
|
|
+ kfree(rndis_device);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+
|
|
+ /* Initialize the rndis device */
|
|
+ net_device = hv_get_drvdata(dev);
|
|
+
|
|
+ net_device->extension = rndis_device;
|
|
+ rndis_device->net_dev = net_device;
|
|
+
|
|
+ /* Send the rndis initialization message */
|
|
+ ret = rndis_filter_init_device(rndis_device);
|
|
+ if (ret != 0) {
|
|
+ /*
|
|
+ * TODO: If rndis init failed, we will need to shut down the
|
|
+ * channel
|
|
+ */
|
|
+ }
|
|
+
|
|
+ /* Get the mac address */
|
|
+ ret = rndis_filter_query_device_mac(rndis_device);
|
|
+ if (ret != 0) {
|
|
+ /*
|
|
+ * TODO: shutdown rndis device and the channel
|
|
+ */
|
|
+ }
|
|
+
|
|
+ memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
|
|
+
|
|
+ rndis_filter_query_device_link_status(rndis_device);
|
|
+
|
|
+ device_info->link_state = rndis_device->link_state;
|
|
+
|
|
+ dev_info(&dev->device, "Device MAC %pM link state %s\n",
|
|
+ rndis_device->hw_mac_adr,
|
|
+ device_info->link_state ? "down" : "up");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void rndis_filter_device_remove(struct hv_device *dev)
|
|
+{
|
|
+ struct netvsc_device *net_dev = hv_get_drvdata(dev);
|
|
+ struct rndis_device *rndis_dev = net_dev->extension;
|
|
+
|
|
+ /* Halt and release the rndis device */
|
|
+ rndis_filter_halt_device(rndis_dev);
|
|
+
|
|
+ kfree(rndis_dev);
|
|
+ net_dev->extension = NULL;
|
|
+
|
|
+ netvsc_device_remove(dev);
|
|
+}
|
|
+
|
|
+
|
|
+int rndis_filter_open(struct hv_device *dev)
|
|
+{
|
|
+ struct netvsc_device *net_device = hv_get_drvdata(dev);
|
|
+
|
|
+ if (!net_device)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return rndis_filter_open_device(net_device->extension);
|
|
+}
|
|
+
|
|
+int rndis_filter_close(struct hv_device *dev)
|
|
+{
|
|
+ struct netvsc_device *netDevice = hv_get_drvdata(dev);
|
|
+
|
|
+ if (!netDevice)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return rndis_filter_close_device(netDevice->extension);
|
|
+}
|
|
+
|
|
+int rndis_filter_send(struct hv_device *dev,
|
|
+ struct hv_netvsc_packet *pkt)
|
|
+{
|
|
+ int ret;
|
|
+ struct rndis_filter_packet *filterPacket;
|
|
+ struct rndis_message *rndisMessage;
|
|
+ struct rndis_packet *rndisPacket;
|
|
+ u32 rndisMessageSize;
|
|
+
|
|
+ /* Add the rndis header */
|
|
+ filterPacket = (struct rndis_filter_packet *)pkt->extension;
|
|
+
|
|
+ memset(filterPacket, 0, sizeof(struct rndis_filter_packet));
|
|
+
|
|
+ rndisMessage = &filterPacket->msg;
|
|
+ rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet);
|
|
+
|
|
+ rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
|
|
+ rndisMessage->msg_len = pkt->total_data_buflen +
|
|
+ rndisMessageSize;
|
|
+
|
|
+ rndisPacket = &rndisMessage->msg.pkt;
|
|
+ rndisPacket->data_offset = sizeof(struct rndis_packet);
|
|
+ rndisPacket->data_len = pkt->total_data_buflen;
|
|
+
|
|
+ pkt->is_data_pkt = true;
|
|
+ pkt->page_buf[0].pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT;
|
|
+ pkt->page_buf[0].offset =
|
|
+ (unsigned long)rndisMessage & (PAGE_SIZE-1);
|
|
+ pkt->page_buf[0].len = rndisMessageSize;
|
|
+
|
|
+ /* Save the packet send completion and context */
|
|
+ filterPacket->completion = pkt->completion.send.send_completion;
|
|
+ filterPacket->completion_ctx =
|
|
+ pkt->completion.send.send_completion_ctx;
|
|
+
|
|
+ /* Use ours */
|
|
+ pkt->completion.send.send_completion = rndis_filter_send_completion;
|
|
+ pkt->completion.send.send_completion_ctx = filterPacket;
|
|
+
|
|
+ ret = netvsc_send(dev, pkt);
|
|
+ if (ret != 0) {
|
|
+ /*
|
|
+ * Reset the completion to originals to allow retries from
|
|
+ * above
|
|
+ */
|
|
+ pkt->completion.send.send_completion =
|
|
+ filterPacket->completion;
|
|
+ pkt->completion.send.send_completion_ctx =
|
|
+ filterPacket->completion_ctx;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void rndis_filter_send_completion(void *ctx)
|
|
+{
|
|
+ struct rndis_filter_packet *filterPacket = ctx;
|
|
+
|
|
+ /* Pass it back to the original handler */
|
|
+ filterPacket->completion(filterPacket->completion_ctx);
|
|
+}
|
|
+
|
|
+
|
|
+static void rndis_filter_send_request_completion(void *ctx)
|
|
+{
|
|
+ /* Noop */
|
|
+}
|
|
diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig
|
|
index 6c0dc30..60ac479 100644
|
|
--- a/drivers/staging/hv/Kconfig
|
|
+++ b/drivers/staging/hv/Kconfig
|
|
@@ -3,9 +3,3 @@ config HYPERV_STORAGE
|
|
depends on HYPERV && SCSI
|
|
help
|
|
Select this option to enable the Hyper-V virtual storage driver.
|
|
-
|
|
-config HYPERV_NET
|
|
- tristate "Microsoft Hyper-V virtual network driver"
|
|
- depends on HYPERV && NET
|
|
- help
|
|
- Select this option to enable the Hyper-V virtual network driver.
|
|
diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile
|
|
index fbe9a42..af95a6b 100644
|
|
--- a/drivers/staging/hv/Makefile
|
|
+++ b/drivers/staging/hv/Makefile
|
|
@@ -1,5 +1,3 @@
|
|
obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
|
|
-obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o
|
|
|
|
hv_storvsc-y := storvsc_drv.o
|
|
-hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o
|
|
diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO
|
|
index ed4d636..fd080cb 100644
|
|
--- a/drivers/staging/hv/TODO
|
|
+++ b/drivers/staging/hv/TODO
|
|
@@ -1,5 +1,4 @@
|
|
TODO:
|
|
- - audit the network driver
|
|
- audit the scsi driver
|
|
|
|
Please send patches for this code to Greg Kroah-Hartman <gregkh@suse.de>,
|
|
diff --git a/drivers/staging/hv/hyperv_net.h b/drivers/staging/hv/hyperv_net.h
|
|
deleted file mode 100644
|
|
index ac1ec84..0000000
|
|
--- a/drivers/staging/hv/hyperv_net.h
|
|
+++ /dev/null
|
|
@@ -1,1058 +0,0 @@
|
|
-/*
|
|
- *
|
|
- * Copyright (c) 2011, Microsoft Corporation.
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or modify it
|
|
- * under the terms and conditions of the GNU General Public License,
|
|
- * version 2, as published by the Free Software Foundation.
|
|
- *
|
|
- * This program is distributed in the hope it will be useful, but WITHOUT
|
|
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
- * more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License along with
|
|
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
- * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
- *
|
|
- * Authors:
|
|
- * Haiyang Zhang <haiyangz@microsoft.com>
|
|
- * Hank Janssen <hjanssen@microsoft.com>
|
|
- * K. Y. Srinivasan <kys@microsoft.com>
|
|
- *
|
|
- */
|
|
-
|
|
-#ifndef _HYPERV_NET_H
|
|
-#define _HYPERV_NET_H
|
|
-
|
|
-#include <linux/list.h>
|
|
-#include <linux/hyperv.h>
|
|
-
|
|
-/* Fwd declaration */
|
|
-struct hv_netvsc_packet;
|
|
-
|
|
-/* Represent the xfer page packet which contains 1 or more netvsc packet */
|
|
-struct xferpage_packet {
|
|
- struct list_head list_ent;
|
|
-
|
|
- /* # of netvsc packets this xfer packet contains */
|
|
- u32 count;
|
|
-};
|
|
-
|
|
-/* The number of pages which are enough to cover jumbo frame buffer. */
|
|
-#define NETVSC_PACKET_MAXPAGE 4
|
|
-
|
|
-/*
|
|
- * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
|
|
- * within the RNDIS
|
|
- */
|
|
-struct hv_netvsc_packet {
|
|
- /* Bookkeeping stuff */
|
|
- struct list_head list_ent;
|
|
-
|
|
- struct hv_device *device;
|
|
- bool is_data_pkt;
|
|
-
|
|
- /*
|
|
- * Valid only for receives when we break a xfer page packet
|
|
- * into multiple netvsc packets
|
|
- */
|
|
- struct xferpage_packet *xfer_page_pkt;
|
|
-
|
|
- union {
|
|
- struct {
|
|
- u64 recv_completion_tid;
|
|
- void *recv_completion_ctx;
|
|
- void (*recv_completion)(void *context);
|
|
- } recv;
|
|
- struct {
|
|
- u64 send_completion_tid;
|
|
- void *send_completion_ctx;
|
|
- void (*send_completion)(void *context);
|
|
- } send;
|
|
- } completion;
|
|
-
|
|
- /* This points to the memory after page_buf */
|
|
- void *extension;
|
|
-
|
|
- u32 total_data_buflen;
|
|
- /* Points to the send/receive buffer where the ethernet frame is */
|
|
- u32 page_buf_cnt;
|
|
- struct hv_page_buffer page_buf[NETVSC_PACKET_MAXPAGE];
|
|
-};
|
|
-
|
|
-struct netvsc_device_info {
|
|
- unsigned char mac_adr[6];
|
|
- bool link_state; /* 0 - link up, 1 - link down */
|
|
- int ring_size;
|
|
-};
|
|
-
|
|
-/* Interface */
|
|
-int netvsc_device_add(struct hv_device *device, void *additional_info);
|
|
-int netvsc_device_remove(struct hv_device *device);
|
|
-int netvsc_send(struct hv_device *device,
|
|
- struct hv_netvsc_packet *packet);
|
|
-void netvsc_linkstatus_callback(struct hv_device *device_obj,
|
|
- unsigned int status);
|
|
-int netvsc_recv_callback(struct hv_device *device_obj,
|
|
- struct hv_netvsc_packet *packet);
|
|
-int rndis_filter_open(struct hv_device *dev);
|
|
-int rndis_filter_close(struct hv_device *dev);
|
|
-int rndis_filter_device_add(struct hv_device *dev,
|
|
- void *additional_info);
|
|
-void rndis_filter_device_remove(struct hv_device *dev);
|
|
-int rndis_filter_receive(struct hv_device *dev,
|
|
- struct hv_netvsc_packet *pkt);
|
|
-
|
|
-
|
|
-
|
|
-int rndis_filter_send(struct hv_device *dev,
|
|
- struct hv_netvsc_packet *pkt);
|
|
-
|
|
-#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF)
|
|
-
|
|
-#define NVSP_PROTOCOL_VERSION_1 2
|
|
-#define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1
|
|
-#define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1
|
|
-
|
|
-enum {
|
|
- NVSP_MSG_TYPE_NONE = 0,
|
|
-
|
|
- /* Init Messages */
|
|
- NVSP_MSG_TYPE_INIT = 1,
|
|
- NVSP_MSG_TYPE_INIT_COMPLETE = 2,
|
|
-
|
|
- NVSP_VERSION_MSG_START = 100,
|
|
-
|
|
- /* Version 1 Messages */
|
|
- NVSP_MSG1_TYPE_SEND_NDIS_VER = NVSP_VERSION_MSG_START,
|
|
-
|
|
- NVSP_MSG1_TYPE_SEND_RECV_BUF,
|
|
- NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE,
|
|
- NVSP_MSG1_TYPE_REVOKE_RECV_BUF,
|
|
-
|
|
- NVSP_MSG1_TYPE_SEND_SEND_BUF,
|
|
- NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE,
|
|
- NVSP_MSG1_TYPE_REVOKE_SEND_BUF,
|
|
-
|
|
- NVSP_MSG1_TYPE_SEND_RNDIS_PKT,
|
|
- NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE,
|
|
-
|
|
- /*
|
|
- * This should be set to the number of messages for the version with
|
|
- * the maximum number of messages.
|
|
- */
|
|
- NVSP_NUM_MSG_PER_VERSION = 9,
|
|
-};
|
|
-
|
|
-enum {
|
|
- NVSP_STAT_NONE = 0,
|
|
- NVSP_STAT_SUCCESS,
|
|
- NVSP_STAT_FAIL,
|
|
- NVSP_STAT_PROTOCOL_TOO_NEW,
|
|
- NVSP_STAT_PROTOCOL_TOO_OLD,
|
|
- NVSP_STAT_INVALID_RNDIS_PKT,
|
|
- NVSP_STAT_BUSY,
|
|
- NVSP_STAT_MAX,
|
|
-};
|
|
-
|
|
-struct nvsp_message_header {
|
|
- u32 msg_type;
|
|
-};
|
|
-
|
|
-/* Init Messages */
|
|
-
|
|
-/*
|
|
- * This message is used by the VSC to initialize the channel after the channels
|
|
- * has been opened. This message should never include anything other then
|
|
- * versioning (i.e. this message will be the same for ever).
|
|
- */
|
|
-struct nvsp_message_init {
|
|
- u32 min_protocol_ver;
|
|
- u32 max_protocol_ver;
|
|
-} __packed;
|
|
-
|
|
-/*
|
|
- * This message is used by the VSP to complete the initialization of the
|
|
- * channel. This message should never include anything other then versioning
|
|
- * (i.e. this message will be the same for ever).
|
|
- */
|
|
-struct nvsp_message_init_complete {
|
|
- u32 negotiated_protocol_ver;
|
|
- u32 max_mdl_chain_len;
|
|
- u32 status;
|
|
-} __packed;
|
|
-
|
|
-union nvsp_message_init_uber {
|
|
- struct nvsp_message_init init;
|
|
- struct nvsp_message_init_complete init_complete;
|
|
-} __packed;
|
|
-
|
|
-/* Version 1 Messages */
|
|
-
|
|
-/*
|
|
- * This message is used by the VSC to send the NDIS version to the VSP. The VSP
|
|
- * can use this information when handling OIDs sent by the VSC.
|
|
- */
|
|
-struct nvsp_1_message_send_ndis_version {
|
|
- u32 ndis_major_ver;
|
|
- u32 ndis_minor_ver;
|
|
-} __packed;
|
|
-
|
|
-/*
|
|
- * This message is used by the VSC to send a receive buffer to the VSP. The VSP
|
|
- * can then use the receive buffer to send data to the VSC.
|
|
- */
|
|
-struct nvsp_1_message_send_receive_buffer {
|
|
- u32 gpadl_handle;
|
|
- u16 id;
|
|
-} __packed;
|
|
-
|
|
-struct nvsp_1_receive_buffer_section {
|
|
- u32 offset;
|
|
- u32 sub_alloc_size;
|
|
- u32 num_sub_allocs;
|
|
- u32 end_offset;
|
|
-} __packed;
|
|
-
|
|
-/*
|
|
- * This message is used by the VSP to acknowledge a receive buffer send by the
|
|
- * VSC. This message must be sent by the VSP before the VSP uses the receive
|
|
- * buffer.
|
|
- */
|
|
-struct nvsp_1_message_send_receive_buffer_complete {
|
|
- u32 status;
|
|
- u32 num_sections;
|
|
-
|
|
- /*
|
|
- * The receive buffer is split into two parts, a large suballocation
|
|
- * section and a small suballocation section. These sections are then
|
|
- * suballocated by a certain size.
|
|
- */
|
|
-
|
|
- /*
|
|
- * For example, the following break up of the receive buffer has 6
|
|
- * large suballocations and 10 small suballocations.
|
|
- */
|
|
-
|
|
- /*
|
|
- * | Large Section | | Small Section |
|
|
- * ------------------------------------------------------------
|
|
- * | | | | | | | | | | | | | | | | | |
|
|
- * | |
|
|
- * LargeOffset SmallOffset
|
|
- */
|
|
-
|
|
- struct nvsp_1_receive_buffer_section sections[1];
|
|
-} __packed;
|
|
-
|
|
-/*
|
|
- * This message is sent by the VSC to revoke the receive buffer. After the VSP
|
|
- * completes this transaction, the vsp should never use the receive buffer
|
|
- * again.
|
|
- */
|
|
-struct nvsp_1_message_revoke_receive_buffer {
|
|
- u16 id;
|
|
-};
|
|
-
|
|
-/*
|
|
- * This message is used by the VSC to send a send buffer to the VSP. The VSC
|
|
- * can then use the send buffer to send data to the VSP.
|
|
- */
|
|
-struct nvsp_1_message_send_send_buffer {
|
|
- u32 gpadl_handle;
|
|
- u16 id;
|
|
-} __packed;
|
|
-
|
|
-/*
|
|
- * This message is used by the VSP to acknowledge a send buffer sent by the
|
|
- * VSC. This message must be sent by the VSP before the VSP uses the sent
|
|
- * buffer.
|
|
- */
|
|
-struct nvsp_1_message_send_send_buffer_complete {
|
|
- u32 status;
|
|
-
|
|
- /*
|
|
- * The VSC gets to choose the size of the send buffer and the VSP gets
|
|
- * to choose the sections size of the buffer. This was done to enable
|
|
- * dynamic reconfigurations when the cost of GPA-direct buffers
|
|
- * decreases.
|
|
- */
|
|
- u32 section_size;
|
|
-} __packed;
|
|
-
|
|
-/*
|
|
- * This message is sent by the VSC to revoke the send buffer. After the VSP
|
|
- * completes this transaction, the vsp should never use the send buffer again.
|
|
- */
|
|
-struct nvsp_1_message_revoke_send_buffer {
|
|
- u16 id;
|
|
-};
|
|
-
|
|
-/*
|
|
- * This message is used by both the VSP and the VSC to send a RNDIS message to
|
|
- * the opposite channel endpoint.
|
|
- */
|
|
-struct nvsp_1_message_send_rndis_packet {
|
|
- /*
|
|
- * This field is specified by RNIDS. They assume there's two different
|
|
- * channels of communication. However, the Network VSP only has one.
|
|
- * Therefore, the channel travels with the RNDIS packet.
|
|
- */
|
|
- u32 channel_type;
|
|
-
|
|
- /*
|
|
- * This field is used to send part or all of the data through a send
|
|
- * buffer. This values specifies an index into the send buffer. If the
|
|
- * index is 0xFFFFFFFF, then the send buffer is not being used and all
|
|
- * of the data was sent through other VMBus mechanisms.
|
|
- */
|
|
- u32 send_buf_section_index;
|
|
- u32 send_buf_section_size;
|
|
-} __packed;
|
|
-
|
|
-/*
|
|
- * This message is used by both the VSP and the VSC to complete a RNDIS message
|
|
- * to the opposite channel endpoint. At this point, the initiator of this
|
|
- * message cannot use any resources associated with the original RNDIS packet.
|
|
- */
|
|
-struct nvsp_1_message_send_rndis_packet_complete {
|
|
- u32 status;
|
|
-};
|
|
-
|
|
-union nvsp_1_message_uber {
|
|
- struct nvsp_1_message_send_ndis_version send_ndis_ver;
|
|
-
|
|
- struct nvsp_1_message_send_receive_buffer send_recv_buf;
|
|
- struct nvsp_1_message_send_receive_buffer_complete
|
|
- send_recv_buf_complete;
|
|
- struct nvsp_1_message_revoke_receive_buffer revoke_recv_buf;
|
|
-
|
|
- struct nvsp_1_message_send_send_buffer send_send_buf;
|
|
- struct nvsp_1_message_send_send_buffer_complete send_send_buf_complete;
|
|
- struct nvsp_1_message_revoke_send_buffer revoke_send_buf;
|
|
-
|
|
- struct nvsp_1_message_send_rndis_packet send_rndis_pkt;
|
|
- struct nvsp_1_message_send_rndis_packet_complete
|
|
- send_rndis_pkt_complete;
|
|
-} __packed;
|
|
-
|
|
-union nvsp_all_messages {
|
|
- union nvsp_message_init_uber init_msg;
|
|
- union nvsp_1_message_uber v1_msg;
|
|
-} __packed;
|
|
-
|
|
-/* ALL Messages */
|
|
-struct nvsp_message {
|
|
- struct nvsp_message_header hdr;
|
|
- union nvsp_all_messages msg;
|
|
-} __packed;
|
|
-
|
|
-
|
|
-
|
|
-
|
|
-/* #define NVSC_MIN_PROTOCOL_VERSION 1 */
|
|
-/* #define NVSC_MAX_PROTOCOL_VERSION 1 */
|
|
-
|
|
-#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */
|
|
-
|
|
-#define NETVSC_RECEIVE_BUFFER_ID 0xcafe
|
|
-
|
|
-#define NETVSC_RECEIVE_SG_COUNT 1
|
|
-
|
|
-/* Preallocated receive packets */
|
|
-#define NETVSC_RECEIVE_PACKETLIST_COUNT 256
|
|
-
|
|
-#define NETVSC_PACKET_SIZE 2048
|
|
-
|
|
-/* Per netvsc channel-specific */
|
|
-struct netvsc_device {
|
|
- struct hv_device *dev;
|
|
-
|
|
- atomic_t num_outstanding_sends;
|
|
- bool destroy;
|
|
- /*
|
|
- * List of free preallocated hv_netvsc_packet to represent receive
|
|
- * packet
|
|
- */
|
|
- struct list_head recv_pkt_list;
|
|
- spinlock_t recv_pkt_list_lock;
|
|
-
|
|
- /* Receive buffer allocated by us but manages by NetVSP */
|
|
- void *recv_buf;
|
|
- u32 recv_buf_size;
|
|
- u32 recv_buf_gpadl_handle;
|
|
- u32 recv_section_cnt;
|
|
- struct nvsp_1_receive_buffer_section *recv_section;
|
|
-
|
|
- /* Used for NetVSP initialization protocol */
|
|
- struct completion channel_init_wait;
|
|
- struct nvsp_message channel_init_pkt;
|
|
-
|
|
- struct nvsp_message revoke_packet;
|
|
- /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */
|
|
-
|
|
- struct net_device *ndev;
|
|
-
|
|
- /* Holds rndis device info */
|
|
- void *extension;
|
|
-};
|
|
-
|
|
-
|
|
-/* Status codes */
|
|
-
|
|
-
|
|
-#ifndef STATUS_SUCCESS
|
|
-#define STATUS_SUCCESS (0x00000000L)
|
|
-#endif
|
|
-
|
|
-#ifndef STATUS_UNSUCCESSFUL
|
|
-#define STATUS_UNSUCCESSFUL (0xC0000001L)
|
|
-#endif
|
|
-
|
|
-#ifndef STATUS_PENDING
|
|
-#define STATUS_PENDING (0x00000103L)
|
|
-#endif
|
|
-
|
|
-#ifndef STATUS_INSUFFICIENT_RESOURCES
|
|
-#define STATUS_INSUFFICIENT_RESOURCES (0xC000009AL)
|
|
-#endif
|
|
-
|
|
-#ifndef STATUS_BUFFER_OVERFLOW
|
|
-#define STATUS_BUFFER_OVERFLOW (0x80000005L)
|
|
-#endif
|
|
-
|
|
-#ifndef STATUS_NOT_SUPPORTED
|
|
-#define STATUS_NOT_SUPPORTED (0xC00000BBL)
|
|
-#endif
|
|
-
|
|
-#define RNDIS_STATUS_SUCCESS (STATUS_SUCCESS)
|
|
-#define RNDIS_STATUS_PENDING (STATUS_PENDING)
|
|
-#define RNDIS_STATUS_NOT_RECOGNIZED (0x00010001L)
|
|
-#define RNDIS_STATUS_NOT_COPIED (0x00010002L)
|
|
-#define RNDIS_STATUS_NOT_ACCEPTED (0x00010003L)
|
|
-#define RNDIS_STATUS_CALL_ACTIVE (0x00010007L)
|
|
-
|
|
-#define RNDIS_STATUS_ONLINE (0x40010003L)
|
|
-#define RNDIS_STATUS_RESET_START (0x40010004L)
|
|
-#define RNDIS_STATUS_RESET_END (0x40010005L)
|
|
-#define RNDIS_STATUS_RING_STATUS (0x40010006L)
|
|
-#define RNDIS_STATUS_CLOSED (0x40010007L)
|
|
-#define RNDIS_STATUS_WAN_LINE_UP (0x40010008L)
|
|
-#define RNDIS_STATUS_WAN_LINE_DOWN (0x40010009L)
|
|
-#define RNDIS_STATUS_WAN_FRAGMENT (0x4001000AL)
|
|
-#define RNDIS_STATUS_MEDIA_CONNECT (0x4001000BL)
|
|
-#define RNDIS_STATUS_MEDIA_DISCONNECT (0x4001000CL)
|
|
-#define RNDIS_STATUS_HARDWARE_LINE_UP (0x4001000DL)
|
|
-#define RNDIS_STATUS_HARDWARE_LINE_DOWN (0x4001000EL)
|
|
-#define RNDIS_STATUS_INTERFACE_UP (0x4001000FL)
|
|
-#define RNDIS_STATUS_INTERFACE_DOWN (0x40010010L)
|
|
-#define RNDIS_STATUS_MEDIA_BUSY (0x40010011L)
|
|
-#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION (0x40010012L)
|
|
-#define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION
|
|
-#define RNDIS_STATUS_LINK_SPEED_CHANGE (0x40010013L)
|
|
-
|
|
-#define RNDIS_STATUS_NOT_RESETTABLE (0x80010001L)
|
|
-#define RNDIS_STATUS_SOFT_ERRORS (0x80010003L)
|
|
-#define RNDIS_STATUS_HARD_ERRORS (0x80010004L)
|
|
-#define RNDIS_STATUS_BUFFER_OVERFLOW (STATUS_BUFFER_OVERFLOW)
|
|
-
|
|
-#define RNDIS_STATUS_FAILURE (STATUS_UNSUCCESSFUL)
|
|
-#define RNDIS_STATUS_RESOURCES (STATUS_INSUFFICIENT_RESOURCES)
|
|
-#define RNDIS_STATUS_CLOSING (0xC0010002L)
|
|
-#define RNDIS_STATUS_BAD_VERSION (0xC0010004L)
|
|
-#define RNDIS_STATUS_BAD_CHARACTERISTICS (0xC0010005L)
|
|
-#define RNDIS_STATUS_ADAPTER_NOT_FOUND (0xC0010006L)
|
|
-#define RNDIS_STATUS_OPEN_FAILED (0xC0010007L)
|
|
-#define RNDIS_STATUS_DEVICE_FAILED (0xC0010008L)
|
|
-#define RNDIS_STATUS_MULTICAST_FULL (0xC0010009L)
|
|
-#define RNDIS_STATUS_MULTICAST_EXISTS (0xC001000AL)
|
|
-#define RNDIS_STATUS_MULTICAST_NOT_FOUND (0xC001000BL)
|
|
-#define RNDIS_STATUS_REQUEST_ABORTED (0xC001000CL)
|
|
-#define RNDIS_STATUS_RESET_IN_PROGRESS (0xC001000DL)
|
|
-#define RNDIS_STATUS_CLOSING_INDICATING (0xC001000EL)
|
|
-#define RNDIS_STATUS_NOT_SUPPORTED (STATUS_NOT_SUPPORTED)
|
|
-#define RNDIS_STATUS_INVALID_PACKET (0xC001000FL)
|
|
-#define RNDIS_STATUS_OPEN_LIST_FULL (0xC0010010L)
|
|
-#define RNDIS_STATUS_ADAPTER_NOT_READY (0xC0010011L)
|
|
-#define RNDIS_STATUS_ADAPTER_NOT_OPEN (0xC0010012L)
|
|
-#define RNDIS_STATUS_NOT_INDICATING (0xC0010013L)
|
|
-#define RNDIS_STATUS_INVALID_LENGTH (0xC0010014L)
|
|
-#define RNDIS_STATUS_INVALID_DATA (0xC0010015L)
|
|
-#define RNDIS_STATUS_BUFFER_TOO_SHORT (0xC0010016L)
|
|
-#define RNDIS_STATUS_INVALID_OID (0xC0010017L)
|
|
-#define RNDIS_STATUS_ADAPTER_REMOVED (0xC0010018L)
|
|
-#define RNDIS_STATUS_UNSUPPORTED_MEDIA (0xC0010019L)
|
|
-#define RNDIS_STATUS_GROUP_ADDRESS_IN_USE (0xC001001AL)
|
|
-#define RNDIS_STATUS_FILE_NOT_FOUND (0xC001001BL)
|
|
-#define RNDIS_STATUS_ERROR_READING_FILE (0xC001001CL)
|
|
-#define RNDIS_STATUS_ALREADY_MAPPED (0xC001001DL)
|
|
-#define RNDIS_STATUS_RESOURCE_CONFLICT (0xC001001EL)
|
|
-#define RNDIS_STATUS_NO_CABLE (0xC001001FL)
|
|
-
|
|
-#define RNDIS_STATUS_INVALID_SAP (0xC0010020L)
|
|
-#define RNDIS_STATUS_SAP_IN_USE (0xC0010021L)
|
|
-#define RNDIS_STATUS_INVALID_ADDRESS (0xC0010022L)
|
|
-#define RNDIS_STATUS_VC_NOT_ACTIVATED (0xC0010023L)
|
|
-#define RNDIS_STATUS_DEST_OUT_OF_ORDER (0xC0010024L)
|
|
-#define RNDIS_STATUS_VC_NOT_AVAILABLE (0xC0010025L)
|
|
-#define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE (0xC0010026L)
|
|
-#define RNDIS_STATUS_INCOMPATABLE_QOS (0xC0010027L)
|
|
-#define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED (0xC0010028L)
|
|
-#define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION (0xC0010029L)
|
|
-
|
|
-#define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR (0xC0011000L)
|
|
-
|
|
-/* Object Identifiers used by NdisRequest Query/Set Information */
|
|
-/* General Objects */
|
|
-#define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101
|
|
-#define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102
|
|
-#define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103
|
|
-#define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104
|
|
-#define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
|
|
-#define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
|
|
-#define RNDIS_OID_GEN_LINK_SPEED 0x00010107
|
|
-#define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
|
|
-#define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
|
|
-#define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
|
|
-#define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
|
|
-#define RNDIS_OID_GEN_VENDOR_ID 0x0001010C
|
|
-#define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D
|
|
-#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
|
|
-#define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
|
|
-#define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110
|
|
-#define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
|
|
-#define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112
|
|
-#define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113
|
|
-#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
|
|
-#define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
|
|
-#define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
|
|
-#define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
|
|
-#define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
|
|
-#define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A
|
|
-#define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
|
|
-
|
|
-#define RNDIS_OID_GEN_XMIT_OK 0x00020101
|
|
-#define RNDIS_OID_GEN_RCV_OK 0x00020102
|
|
-#define RNDIS_OID_GEN_XMIT_ERROR 0x00020103
|
|
-#define RNDIS_OID_GEN_RCV_ERROR 0x00020104
|
|
-#define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105
|
|
-
|
|
-#define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
|
|
-#define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
|
|
-#define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
|
|
-#define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
|
|
-#define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
|
|
-#define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
|
|
-#define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207
|
|
-#define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
|
|
-#define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209
|
|
-#define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
|
|
-#define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
|
|
-#define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
|
|
-
|
|
-#define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D
|
|
-#define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
|
|
-
|
|
-#define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F
|
|
-#define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210
|
|
-
|
|
-/* These are connection-oriented general OIDs. */
|
|
-/* These replace the above OIDs for connection-oriented media. */
|
|
-#define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101
|
|
-#define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102
|
|
-#define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103
|
|
-#define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104
|
|
-#define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105
|
|
-#define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106
|
|
-#define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107
|
|
-#define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108
|
|
-#define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109
|
|
-#define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A
|
|
-#define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B
|
|
-#define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C
|
|
-#define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D
|
|
-
|
|
-#define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201
|
|
-#define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202
|
|
-
|
|
-/* These are connection-oriented statistics OIDs. */
|
|
-#define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101
|
|
-#define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102
|
|
-#define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103
|
|
-#define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104
|
|
-#define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105
|
|
-
|
|
-
|
|
-#define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201
|
|
-#define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202
|
|
-#define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203
|
|
-#define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204
|
|
-#define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205
|
|
-#define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206
|
|
-
|
|
-/* These are objects for Connection-oriented media call-managers. */
|
|
-#define RNDIS_OID_CO_ADD_PVC 0xFF000001
|
|
-#define RNDIS_OID_CO_DELETE_PVC 0xFF000002
|
|
-#define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003
|
|
-#define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004
|
|
-#define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005
|
|
-#define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006
|
|
-#define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007
|
|
-#define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008
|
|
-#define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009
|
|
-
|
|
-/* 802.3 Objects (Ethernet) */
|
|
-#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101
|
|
-#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102
|
|
-#define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103
|
|
-#define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
|
|
-#define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105
|
|
-
|
|
-#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
|
|
-
|
|
-#define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
|
|
-#define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102
|
|
-#define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
|
|
-
|
|
-#define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201
|
|
-#define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
|
|
-#define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203
|
|
-#define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204
|
|
-#define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
|
|
-#define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
|
|
-#define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
|
|
-
|
|
-/* Remote NDIS message types */
|
|
-#define REMOTE_NDIS_PACKET_MSG 0x00000001
|
|
-#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002
|
|
-#define REMOTE_NDIS_HALT_MSG 0x00000003
|
|
-#define REMOTE_NDIS_QUERY_MSG 0x00000004
|
|
-#define REMOTE_NDIS_SET_MSG 0x00000005
|
|
-#define REMOTE_NDIS_RESET_MSG 0x00000006
|
|
-#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007
|
|
-#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008
|
|
-
|
|
-#define REMOTE_CONDIS_MP_CREATE_VC_MSG 0x00008001
|
|
-#define REMOTE_CONDIS_MP_DELETE_VC_MSG 0x00008002
|
|
-#define REMOTE_CONDIS_MP_ACTIVATE_VC_MSG 0x00008005
|
|
-#define REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG 0x00008006
|
|
-#define REMOTE_CONDIS_INDICATE_STATUS_MSG 0x00008007
|
|
-
|
|
-/* Remote NDIS message completion types */
|
|
-#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002
|
|
-#define REMOTE_NDIS_QUERY_CMPLT 0x80000004
|
|
-#define REMOTE_NDIS_SET_CMPLT 0x80000005
|
|
-#define REMOTE_NDIS_RESET_CMPLT 0x80000006
|
|
-#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008
|
|
-
|
|
-#define REMOTE_CONDIS_MP_CREATE_VC_CMPLT 0x80008001
|
|
-#define REMOTE_CONDIS_MP_DELETE_VC_CMPLT 0x80008002
|
|
-#define REMOTE_CONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005
|
|
-#define REMOTE_CONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006
|
|
-
|
|
-/*
|
|
- * Reserved message type for private communication between lower-layer host
|
|
- * driver and remote device, if necessary.
|
|
- */
|
|
-#define REMOTE_NDIS_BUS_MSG 0xff000001
|
|
-
|
|
-/* Defines for DeviceFlags in struct rndis_initialize_complete */
|
|
-#define RNDIS_DF_CONNECTIONLESS 0x00000001
|
|
-#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002
|
|
-#define RNDIS_DF_RAW_DATA 0x00000004
|
|
-
|
|
-/* Remote NDIS medium types. */
|
|
-#define RNDIS_MEDIUM_802_3 0x00000000
|
|
-#define RNDIS_MEDIUM_802_5 0x00000001
|
|
-#define RNDIS_MEDIUM_FDDI 0x00000002
|
|
-#define RNDIS_MEDIUM_WAN 0x00000003
|
|
-#define RNDIS_MEDIUM_LOCAL_TALK 0x00000004
|
|
-#define RNDIS_MEDIUM_ARCNET_RAW 0x00000006
|
|
-#define RNDIS_MEDIUM_ARCNET_878_2 0x00000007
|
|
-#define RNDIS_MEDIUM_ATM 0x00000008
|
|
-#define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009
|
|
-#define RNDIS_MEDIUM_IRDA 0x0000000a
|
|
-#define RNDIS_MEDIUM_CO_WAN 0x0000000b
|
|
-/* Not a real medium, defined as an upper-bound */
|
|
-#define RNDIS_MEDIUM_MAX 0x0000000d
|
|
-
|
|
-
|
|
-/* Remote NDIS medium connection states. */
|
|
-#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000
|
|
-#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001
|
|
-
|
|
-/* Remote NDIS version numbers */
|
|
-#define RNDIS_MAJOR_VERSION 0x00000001
|
|
-#define RNDIS_MINOR_VERSION 0x00000000
|
|
-
|
|
-
|
|
-/* NdisInitialize message */
|
|
-struct rndis_initialize_request {
|
|
- u32 req_id;
|
|
- u32 major_ver;
|
|
- u32 minor_ver;
|
|
- u32 max_xfer_size;
|
|
-};
|
|
-
|
|
-/* Response to NdisInitialize */
|
|
-struct rndis_initialize_complete {
|
|
- u32 req_id;
|
|
- u32 status;
|
|
- u32 major_ver;
|
|
- u32 minor_ver;
|
|
- u32 dev_flags;
|
|
- u32 medium;
|
|
- u32 max_pkt_per_msg;
|
|
- u32 max_xfer_size;
|
|
- u32 pkt_alignment_factor;
|
|
- u32 af_list_offset;
|
|
- u32 af_list_size;
|
|
-};
|
|
-
|
|
-/* Call manager devices only: Information about an address family */
|
|
-/* supported by the device is appended to the response to NdisInitialize. */
|
|
-struct rndis_co_address_family {
|
|
- u32 address_family;
|
|
- u32 major_ver;
|
|
- u32 minor_ver;
|
|
-};
|
|
-
|
|
-/* NdisHalt message */
|
|
-struct rndis_halt_request {
|
|
- u32 req_id;
|
|
-};
|
|
-
|
|
-/* NdisQueryRequest message */
|
|
-struct rndis_query_request {
|
|
- u32 req_id;
|
|
- u32 oid;
|
|
- u32 info_buflen;
|
|
- u32 info_buf_offset;
|
|
- u32 dev_vc_handle;
|
|
-};
|
|
-
|
|
-/* Response to NdisQueryRequest */
|
|
-struct rndis_query_complete {
|
|
- u32 req_id;
|
|
- u32 status;
|
|
- u32 info_buflen;
|
|
- u32 info_buf_offset;
|
|
-};
|
|
-
|
|
-/* NdisSetRequest message */
|
|
-struct rndis_set_request {
|
|
- u32 req_id;
|
|
- u32 oid;
|
|
- u32 info_buflen;
|
|
- u32 info_buf_offset;
|
|
- u32 dev_vc_handle;
|
|
-};
|
|
-
|
|
-/* Response to NdisSetRequest */
|
|
-struct rndis_set_complete {
|
|
- u32 req_id;
|
|
- u32 status;
|
|
-};
|
|
-
|
|
-/* NdisReset message */
|
|
-struct rndis_reset_request {
|
|
- u32 reserved;
|
|
-};
|
|
-
|
|
-/* Response to NdisReset */
|
|
-struct rndis_reset_complete {
|
|
- u32 status;
|
|
- u32 addressing_reset;
|
|
-};
|
|
-
|
|
-/* NdisMIndicateStatus message */
|
|
-struct rndis_indicate_status {
|
|
- u32 status;
|
|
- u32 status_buflen;
|
|
- u32 status_buf_offset;
|
|
-};
|
|
-
|
|
-/* Diagnostic information passed as the status buffer in */
|
|
-/* struct rndis_indicate_status messages signifying error conditions. */
|
|
-struct rndis_diagnostic_info {
|
|
- u32 diag_status;
|
|
- u32 error_offset;
|
|
-};
|
|
-
|
|
-/* NdisKeepAlive message */
|
|
-struct rndis_keepalive_request {
|
|
- u32 req_id;
|
|
-};
|
|
-
|
|
-/* Response to NdisKeepAlive */
|
|
-struct rndis_keepalive_complete {
|
|
- u32 req_id;
|
|
- u32 status;
|
|
-};
|
|
-
|
|
-/*
|
|
- * Data message. All Offset fields contain byte offsets from the beginning of
|
|
- * struct rndis_packet. All Length fields are in bytes. VcHandle is set
|
|
- * to 0 for connectionless data, otherwise it contains the VC handle.
|
|
- */
|
|
-struct rndis_packet {
|
|
- u32 data_offset;
|
|
- u32 data_len;
|
|
- u32 oob_data_offset;
|
|
- u32 oob_data_len;
|
|
- u32 num_oob_data_elements;
|
|
- u32 per_pkt_info_offset;
|
|
- u32 per_pkt_info_len;
|
|
- u32 vc_handle;
|
|
- u32 reserved;
|
|
-};
|
|
-
|
|
-/* Optional Out of Band data associated with a Data message. */
|
|
-struct rndis_oobd {
|
|
- u32 size;
|
|
- u32 type;
|
|
- u32 class_info_offset;
|
|
-};
|
|
-
|
|
-/* Packet extension field contents associated with a Data message. */
|
|
-struct rndis_per_packet_info {
|
|
- u32 size;
|
|
- u32 type;
|
|
- u32 per_pkt_info_offset;
|
|
-};
|
|
-
|
|
-/* Format of Information buffer passed in a SetRequest for the OID */
|
|
-/* OID_GEN_RNDIS_CONFIG_PARAMETER. */
|
|
-struct rndis_config_parameter_info {
|
|
- u32 parameter_name_offset;
|
|
- u32 parameter_name_length;
|
|
- u32 parameter_type;
|
|
- u32 parameter_value_offset;
|
|
- u32 parameter_value_length;
|
|
-};
|
|
-
|
|
-/* Values for ParameterType in struct rndis_config_parameter_info */
|
|
-#define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0
|
|
-#define RNDIS_CONFIG_PARAM_TYPE_STRING 2
|
|
-
|
|
-/* CONDIS Miniport messages for connection oriented devices */
|
|
-/* that do not implement a call manager. */
|
|
-
|
|
-/* CoNdisMiniportCreateVc message */
|
|
-struct rcondis_mp_create_vc {
|
|
- u32 req_id;
|
|
- u32 ndis_vc_handle;
|
|
-};
|
|
-
|
|
-/* Response to CoNdisMiniportCreateVc */
|
|
-struct rcondis_mp_create_vc_complete {
|
|
- u32 req_id;
|
|
- u32 dev_vc_handle;
|
|
- u32 status;
|
|
-};
|
|
-
|
|
-/* CoNdisMiniportDeleteVc message */
|
|
-struct rcondis_mp_delete_vc {
|
|
- u32 req_id;
|
|
- u32 dev_vc_handle;
|
|
-};
|
|
-
|
|
-/* Response to CoNdisMiniportDeleteVc */
|
|
-struct rcondis_mp_delete_vc_complete {
|
|
- u32 req_id;
|
|
- u32 status;
|
|
-};
|
|
-
|
|
-/* CoNdisMiniportQueryRequest message */
|
|
-struct rcondis_mp_query_request {
|
|
- u32 req_id;
|
|
- u32 request_type;
|
|
- u32 oid;
|
|
- u32 dev_vc_handle;
|
|
- u32 info_buflen;
|
|
- u32 info_buf_offset;
|
|
-};
|
|
-
|
|
-/* CoNdisMiniportSetRequest message */
|
|
-struct rcondis_mp_set_request {
|
|
- u32 req_id;
|
|
- u32 request_type;
|
|
- u32 oid;
|
|
- u32 dev_vc_handle;
|
|
- u32 info_buflen;
|
|
- u32 info_buf_offset;
|
|
-};
|
|
-
|
|
-/* CoNdisIndicateStatus message */
|
|
-struct rcondis_indicate_status {
|
|
- u32 ndis_vc_handle;
|
|
- u32 status;
|
|
- u32 status_buflen;
|
|
- u32 status_buf_offset;
|
|
-};
|
|
-
|
|
-/* CONDIS Call/VC parameters */
|
|
-struct rcondis_specific_parameters {
|
|
- u32 parameter_type;
|
|
- u32 parameter_length;
|
|
- u32 parameter_lffset;
|
|
-};
|
|
-
|
|
-struct rcondis_media_parameters {
|
|
- u32 flags;
|
|
- u32 reserved1;
|
|
- u32 reserved2;
|
|
- struct rcondis_specific_parameters media_specific;
|
|
-};
|
|
-
|
|
-struct rndis_flowspec {
|
|
- u32 token_rate;
|
|
- u32 token_bucket_size;
|
|
- u32 peak_bandwidth;
|
|
- u32 latency;
|
|
- u32 delay_variation;
|
|
- u32 service_type;
|
|
- u32 max_sdu_size;
|
|
- u32 minimum_policed_size;
|
|
-};
|
|
-
|
|
-struct rcondis_call_manager_parameters {
|
|
- struct rndis_flowspec transmit;
|
|
- struct rndis_flowspec receive;
|
|
- struct rcondis_specific_parameters call_mgr_specific;
|
|
-};
|
|
-
|
|
-/* CoNdisMiniportActivateVc message */
|
|
-struct rcondis_mp_activate_vc_request {
|
|
- u32 req_id;
|
|
- u32 flags;
|
|
- u32 dev_vc_handle;
|
|
- u32 media_params_offset;
|
|
- u32 media_params_length;
|
|
- u32 call_mgr_params_offset;
|
|
- u32 call_mgr_params_length;
|
|
-};
|
|
-
|
|
-/* Response to CoNdisMiniportActivateVc */
|
|
-struct rcondis_mp_activate_vc_complete {
|
|
- u32 req_id;
|
|
- u32 status;
|
|
-};
|
|
-
|
|
-/* CoNdisMiniportDeactivateVc message */
|
|
-struct rcondis_mp_deactivate_vc_request {
|
|
- u32 req_id;
|
|
- u32 flags;
|
|
- u32 dev_vc_handle;
|
|
-};
|
|
-
|
|
-/* Response to CoNdisMiniportDeactivateVc */
|
|
-struct rcondis_mp_deactivate_vc_complete {
|
|
- u32 req_id;
|
|
- u32 status;
|
|
-};
|
|
-
|
|
-
|
|
-/* union with all of the RNDIS messages */
|
|
-union rndis_message_container {
|
|
- struct rndis_packet pkt;
|
|
- struct rndis_initialize_request init_req;
|
|
- struct rndis_halt_request halt_req;
|
|
- struct rndis_query_request query_req;
|
|
- struct rndis_set_request set_req;
|
|
- struct rndis_reset_request reset_req;
|
|
- struct rndis_keepalive_request keep_alive_req;
|
|
- struct rndis_indicate_status indicate_status;
|
|
- struct rndis_initialize_complete init_complete;
|
|
- struct rndis_query_complete query_complete;
|
|
- struct rndis_set_complete set_complete;
|
|
- struct rndis_reset_complete reset_complete;
|
|
- struct rndis_keepalive_complete keep_alive_complete;
|
|
- struct rcondis_mp_create_vc co_miniport_create_vc;
|
|
- struct rcondis_mp_delete_vc co_miniport_delete_vc;
|
|
- struct rcondis_indicate_status co_indicate_status;
|
|
- struct rcondis_mp_activate_vc_request co_miniport_activate_vc;
|
|
- struct rcondis_mp_deactivate_vc_request co_miniport_deactivate_vc;
|
|
- struct rcondis_mp_create_vc_complete co_miniport_create_vc_complete;
|
|
- struct rcondis_mp_delete_vc_complete co_miniport_delete_vc_complete;
|
|
- struct rcondis_mp_activate_vc_complete co_miniport_activate_vc_complete;
|
|
- struct rcondis_mp_deactivate_vc_complete
|
|
- co_miniport_deactivate_vc_complete;
|
|
-};
|
|
-
|
|
-/* Remote NDIS message format */
|
|
-struct rndis_message {
|
|
- u32 ndis_msg_type;
|
|
-
|
|
- /* Total length of this message, from the beginning */
|
|
- /* of the sruct rndis_message, in bytes. */
|
|
- u32 msg_len;
|
|
-
|
|
- /* Actual message */
|
|
- union rndis_message_container msg;
|
|
-};
|
|
-
|
|
-
|
|
-struct rndis_filter_packet {
|
|
- void *completion_ctx;
|
|
- void (*completion)(void *context);
|
|
- struct rndis_message msg;
|
|
-};
|
|
-
|
|
-/* Handy macros */
|
|
-
|
|
-/* get the size of an RNDIS message. Pass in the message type, */
|
|
-/* struct rndis_set_request, struct rndis_packet for example */
|
|
-#define RNDIS_MESSAGE_SIZE(msg) \
|
|
- (sizeof(msg) + (sizeof(struct rndis_message) - \
|
|
- sizeof(union rndis_message_container)))
|
|
-
|
|
-/* get pointer to info buffer with message pointer */
|
|
-#define MESSAGE_TO_INFO_BUFFER(msg) \
|
|
- (((unsigned char *)(msg)) + msg->info_buf_offset)
|
|
-
|
|
-/* get pointer to status buffer with message pointer */
|
|
-#define MESSAGE_TO_STATUS_BUFFER(msg) \
|
|
- (((unsigned char *)(msg)) + msg->status_buf_offset)
|
|
-
|
|
-/* get pointer to OOBD buffer with message pointer */
|
|
-#define MESSAGE_TO_OOBD_BUFFER(msg) \
|
|
- (((unsigned char *)(msg)) + msg->oob_data_offset)
|
|
-
|
|
-/* get pointer to data buffer with message pointer */
|
|
-#define MESSAGE_TO_DATA_BUFFER(msg) \
|
|
- (((unsigned char *)(msg)) + msg->per_pkt_info_offset)
|
|
-
|
|
-/* get pointer to contained message from NDIS_MESSAGE pointer */
|
|
-#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(rndis_msg) \
|
|
- ((void *) &rndis_msg->msg)
|
|
-
|
|
-/* get pointer to contained message from NDIS_MESSAGE pointer */
|
|
-#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(rndis_msg) \
|
|
- ((void *) rndis_msg)
|
|
-
|
|
-
|
|
-#define __struct_bcount(x)
|
|
-
|
|
-
|
|
-
|
|
-#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \
|
|
- sizeof(union rndis_message_container))
|
|
-
|
|
-#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
|
|
-#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
|
|
-#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
|
|
-#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
|
|
-#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
|
|
-#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
|
|
-#define NDIS_PACKET_TYPE_SMT 0x00000040
|
|
-#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
|
|
-#define NDIS_PACKET_TYPE_GROUP 0x00000100
|
|
-#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
|
|
-#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
|
|
-#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
|
|
-
|
|
-
|
|
-
|
|
-#endif /* _HYPERV_NET_H */
|
|
diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c
|
|
deleted file mode 100644
|
|
index 28e69a6..0000000
|
|
--- a/drivers/staging/hv/netvsc.c
|
|
+++ /dev/null
|
|
@@ -1,939 +0,0 @@
|
|
-/*
|
|
- * Copyright (c) 2009, Microsoft Corporation.
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or modify it
|
|
- * under the terms and conditions of the GNU General Public License,
|
|
- * version 2, as published by the Free Software Foundation.
|
|
- *
|
|
- * This program is distributed in the hope it will be useful, but WITHOUT
|
|
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
- * more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License along with
|
|
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
- * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
- *
|
|
- * Authors:
|
|
- * Haiyang Zhang <haiyangz@microsoft.com>
|
|
- * Hank Janssen <hjanssen@microsoft.com>
|
|
- */
|
|
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
-
|
|
-#include <linux/kernel.h>
|
|
-#include <linux/sched.h>
|
|
-#include <linux/wait.h>
|
|
-#include <linux/mm.h>
|
|
-#include <linux/delay.h>
|
|
-#include <linux/io.h>
|
|
-#include <linux/slab.h>
|
|
-#include <linux/netdevice.h>
|
|
-
|
|
-#include "hyperv_net.h"
|
|
-
|
|
-
|
|
-static struct netvsc_device *alloc_net_device(struct hv_device *device)
|
|
-{
|
|
- struct netvsc_device *net_device;
|
|
- struct net_device *ndev = hv_get_drvdata(device);
|
|
-
|
|
- net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
|
|
- if (!net_device)
|
|
- return NULL;
|
|
-
|
|
-
|
|
- net_device->destroy = false;
|
|
- net_device->dev = device;
|
|
- net_device->ndev = ndev;
|
|
-
|
|
- hv_set_drvdata(device, net_device);
|
|
- return net_device;
|
|
-}
|
|
-
|
|
-static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
|
|
-{
|
|
- struct netvsc_device *net_device;
|
|
-
|
|
- net_device = hv_get_drvdata(device);
|
|
- if (net_device && net_device->destroy)
|
|
- net_device = NULL;
|
|
-
|
|
- return net_device;
|
|
-}
|
|
-
|
|
-static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
|
|
-{
|
|
- struct netvsc_device *net_device;
|
|
-
|
|
- net_device = hv_get_drvdata(device);
|
|
-
|
|
- if (!net_device)
|
|
- goto get_in_err;
|
|
-
|
|
- if (net_device->destroy &&
|
|
- atomic_read(&net_device->num_outstanding_sends) == 0)
|
|
- net_device = NULL;
|
|
-
|
|
-get_in_err:
|
|
- return net_device;
|
|
-}
|
|
-
|
|
-
|
|
-static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
|
|
-{
|
|
- struct nvsp_message *revoke_packet;
|
|
- int ret = 0;
|
|
- struct net_device *ndev = net_device->ndev;
|
|
-
|
|
- /*
|
|
- * If we got a section count, it means we received a
|
|
- * SendReceiveBufferComplete msg (ie sent
|
|
- * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need
|
|
- * to send a revoke msg here
|
|
- */
|
|
- if (net_device->recv_section_cnt) {
|
|
- /* Send the revoke receive buffer */
|
|
- revoke_packet = &net_device->revoke_packet;
|
|
- memset(revoke_packet, 0, sizeof(struct nvsp_message));
|
|
-
|
|
- revoke_packet->hdr.msg_type =
|
|
- NVSP_MSG1_TYPE_REVOKE_RECV_BUF;
|
|
- revoke_packet->msg.v1_msg.
|
|
- revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
|
|
-
|
|
- ret = vmbus_sendpacket(net_device->dev->channel,
|
|
- revoke_packet,
|
|
- sizeof(struct nvsp_message),
|
|
- (unsigned long)revoke_packet,
|
|
- VM_PKT_DATA_INBAND, 0);
|
|
- /*
|
|
- * If we failed here, we might as well return and
|
|
- * have a leak rather than continue and a bugchk
|
|
- */
|
|
- if (ret != 0) {
|
|
- netdev_err(ndev, "unable to send "
|
|
- "revoke receive buffer to netvsp\n");
|
|
- return ret;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Teardown the gpadl on the vsp end */
|
|
- if (net_device->recv_buf_gpadl_handle) {
|
|
- ret = vmbus_teardown_gpadl(net_device->dev->channel,
|
|
- net_device->recv_buf_gpadl_handle);
|
|
-
|
|
- /* If we failed here, we might as well return and have a leak
|
|
- * rather than continue and a bugchk
|
|
- */
|
|
- if (ret != 0) {
|
|
- netdev_err(ndev,
|
|
- "unable to teardown receive buffer's gpadl\n");
|
|
- return ret;
|
|
- }
|
|
- net_device->recv_buf_gpadl_handle = 0;
|
|
- }
|
|
-
|
|
- if (net_device->recv_buf) {
|
|
- /* Free up the receive buffer */
|
|
- free_pages((unsigned long)net_device->recv_buf,
|
|
- get_order(net_device->recv_buf_size));
|
|
- net_device->recv_buf = NULL;
|
|
- }
|
|
-
|
|
- if (net_device->recv_section) {
|
|
- net_device->recv_section_cnt = 0;
|
|
- kfree(net_device->recv_section);
|
|
- net_device->recv_section = NULL;
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int netvsc_init_recv_buf(struct hv_device *device)
|
|
-{
|
|
- int ret = 0;
|
|
- int t;
|
|
- struct netvsc_device *net_device;
|
|
- struct nvsp_message *init_packet;
|
|
- struct net_device *ndev;
|
|
-
|
|
- net_device = get_outbound_net_device(device);
|
|
- if (!net_device)
|
|
- return -ENODEV;
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- net_device->recv_buf =
|
|
- (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
|
|
- get_order(net_device->recv_buf_size));
|
|
- if (!net_device->recv_buf) {
|
|
- netdev_err(ndev, "unable to allocate receive "
|
|
- "buffer of size %d\n", net_device->recv_buf_size);
|
|
- ret = -ENOMEM;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Establish the gpadl handle for this buffer on this
|
|
- * channel. Note: This call uses the vmbus connection rather
|
|
- * than the channel to establish the gpadl handle.
|
|
- */
|
|
- ret = vmbus_establish_gpadl(device->channel, net_device->recv_buf,
|
|
- net_device->recv_buf_size,
|
|
- &net_device->recv_buf_gpadl_handle);
|
|
- if (ret != 0) {
|
|
- netdev_err(ndev,
|
|
- "unable to establish receive buffer's gpadl\n");
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
-
|
|
- /* Notify the NetVsp of the gpadl handle */
|
|
- init_packet = &net_device->channel_init_pkt;
|
|
-
|
|
- memset(init_packet, 0, sizeof(struct nvsp_message));
|
|
-
|
|
- init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_RECV_BUF;
|
|
- init_packet->msg.v1_msg.send_recv_buf.
|
|
- gpadl_handle = net_device->recv_buf_gpadl_handle;
|
|
- init_packet->msg.v1_msg.
|
|
- send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;
|
|
-
|
|
- /* Send the gpadl notification request */
|
|
- ret = vmbus_sendpacket(device->channel, init_packet,
|
|
- sizeof(struct nvsp_message),
|
|
- (unsigned long)init_packet,
|
|
- VM_PKT_DATA_INBAND,
|
|
- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
|
- if (ret != 0) {
|
|
- netdev_err(ndev,
|
|
- "unable to send receive buffer's gpadl to netvsp\n");
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
|
|
- BUG_ON(t == 0);
|
|
-
|
|
-
|
|
- /* Check the response */
|
|
- if (init_packet->msg.v1_msg.
|
|
- send_recv_buf_complete.status != NVSP_STAT_SUCCESS) {
|
|
- netdev_err(ndev, "Unable to complete receive buffer "
|
|
- "initialization with NetVsp - status %d\n",
|
|
- init_packet->msg.v1_msg.
|
|
- send_recv_buf_complete.status);
|
|
- ret = -EINVAL;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /* Parse the response */
|
|
-
|
|
- net_device->recv_section_cnt = init_packet->msg.
|
|
- v1_msg.send_recv_buf_complete.num_sections;
|
|
-
|
|
- net_device->recv_section = kmemdup(init_packet->msg.v1_msg.send_recv_buf_complete.sections,
|
|
- net_device->recv_section_cnt * sizeof(struct nvsp_1_receive_buffer_section),
|
|
- GFP_KERNEL);
|
|
- if (net_device->recv_section == NULL) {
|
|
- ret = -EINVAL;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /*
|
|
- * For 1st release, there should only be 1 section that represents the
|
|
- * entire receive buffer
|
|
- */
|
|
- if (net_device->recv_section_cnt != 1 ||
|
|
- net_device->recv_section->offset != 0) {
|
|
- ret = -EINVAL;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- goto exit;
|
|
-
|
|
-cleanup:
|
|
- netvsc_destroy_recv_buf(net_device);
|
|
-
|
|
-exit:
|
|
- return ret;
|
|
-}
|
|
-
|
|
-
|
|
-static int netvsc_connect_vsp(struct hv_device *device)
|
|
-{
|
|
- int ret, t;
|
|
- struct netvsc_device *net_device;
|
|
- struct nvsp_message *init_packet;
|
|
- int ndis_version;
|
|
- struct net_device *ndev;
|
|
-
|
|
- net_device = get_outbound_net_device(device);
|
|
- if (!net_device)
|
|
- return -ENODEV;
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- init_packet = &net_device->channel_init_pkt;
|
|
-
|
|
- memset(init_packet, 0, sizeof(struct nvsp_message));
|
|
- init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT;
|
|
- init_packet->msg.init_msg.init.min_protocol_ver =
|
|
- NVSP_MIN_PROTOCOL_VERSION;
|
|
- init_packet->msg.init_msg.init.max_protocol_ver =
|
|
- NVSP_MAX_PROTOCOL_VERSION;
|
|
-
|
|
- /* Send the init request */
|
|
- ret = vmbus_sendpacket(device->channel, init_packet,
|
|
- sizeof(struct nvsp_message),
|
|
- (unsigned long)init_packet,
|
|
- VM_PKT_DATA_INBAND,
|
|
- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
|
-
|
|
- if (ret != 0)
|
|
- goto cleanup;
|
|
-
|
|
- t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
|
|
-
|
|
- if (t == 0) {
|
|
- ret = -ETIMEDOUT;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- if (init_packet->msg.init_msg.init_complete.status !=
|
|
- NVSP_STAT_SUCCESS) {
|
|
- ret = -EINVAL;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- if (init_packet->msg.init_msg.init_complete.
|
|
- negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) {
|
|
- ret = -EPROTO;
|
|
- goto cleanup;
|
|
- }
|
|
- /* Send the ndis version */
|
|
- memset(init_packet, 0, sizeof(struct nvsp_message));
|
|
-
|
|
- ndis_version = 0x00050000;
|
|
-
|
|
- init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER;
|
|
- init_packet->msg.v1_msg.
|
|
- send_ndis_ver.ndis_major_ver =
|
|
- (ndis_version & 0xFFFF0000) >> 16;
|
|
- init_packet->msg.v1_msg.
|
|
- send_ndis_ver.ndis_minor_ver =
|
|
- ndis_version & 0xFFFF;
|
|
-
|
|
- /* Send the init request */
|
|
- ret = vmbus_sendpacket(device->channel, init_packet,
|
|
- sizeof(struct nvsp_message),
|
|
- (unsigned long)init_packet,
|
|
- VM_PKT_DATA_INBAND, 0);
|
|
- if (ret != 0)
|
|
- goto cleanup;
|
|
-
|
|
- /* Post the big receive buffer to NetVSP */
|
|
- ret = netvsc_init_recv_buf(device);
|
|
-
|
|
-cleanup:
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
|
|
-{
|
|
- netvsc_destroy_recv_buf(net_device);
|
|
-}
|
|
-
|
|
-/*
|
|
- * netvsc_device_remove - Callback when the root bus device is removed
|
|
- */
|
|
-int netvsc_device_remove(struct hv_device *device)
|
|
-{
|
|
- struct netvsc_device *net_device;
|
|
- struct hv_netvsc_packet *netvsc_packet, *pos;
|
|
- unsigned long flags;
|
|
-
|
|
- net_device = hv_get_drvdata(device);
|
|
- spin_lock_irqsave(&device->channel->inbound_lock, flags);
|
|
- net_device->destroy = true;
|
|
- spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
|
|
-
|
|
- /* Wait for all send completions */
|
|
- while (atomic_read(&net_device->num_outstanding_sends)) {
|
|
- dev_info(&device->device,
|
|
- "waiting for %d requests to complete...\n",
|
|
- atomic_read(&net_device->num_outstanding_sends));
|
|
- udelay(100);
|
|
- }
|
|
-
|
|
- netvsc_disconnect_vsp(net_device);
|
|
-
|
|
- /*
|
|
- * Since we have already drained, we don't need to busy wait
|
|
- * as was done in final_release_stor_device()
|
|
- * Note that we cannot set the ext pointer to NULL until
|
|
- * we have drained - to drain the outgoing packets, we need to
|
|
- * allow incoming packets.
|
|
- */
|
|
-
|
|
- spin_lock_irqsave(&device->channel->inbound_lock, flags);
|
|
- hv_set_drvdata(device, NULL);
|
|
- spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
|
|
-
|
|
- /*
|
|
- * At this point, no one should be accessing net_device
|
|
- * except in here
|
|
- */
|
|
- dev_notice(&device->device, "net device safe to remove\n");
|
|
-
|
|
- /* Now, we can close the channel safely */
|
|
- vmbus_close(device->channel);
|
|
-
|
|
- /* Release all resources */
|
|
- list_for_each_entry_safe(netvsc_packet, pos,
|
|
- &net_device->recv_pkt_list, list_ent) {
|
|
- list_del(&netvsc_packet->list_ent);
|
|
- kfree(netvsc_packet);
|
|
- }
|
|
-
|
|
- kfree(net_device);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void netvsc_send_completion(struct hv_device *device,
|
|
- struct vmpacket_descriptor *packet)
|
|
-{
|
|
- struct netvsc_device *net_device;
|
|
- struct nvsp_message *nvsp_packet;
|
|
- struct hv_netvsc_packet *nvsc_packet;
|
|
- struct net_device *ndev;
|
|
-
|
|
- net_device = get_inbound_net_device(device);
|
|
- if (!net_device)
|
|
- return;
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
|
|
- (packet->offset8 << 3));
|
|
-
|
|
- if ((nvsp_packet->hdr.msg_type == NVSP_MSG_TYPE_INIT_COMPLETE) ||
|
|
- (nvsp_packet->hdr.msg_type ==
|
|
- NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) ||
|
|
- (nvsp_packet->hdr.msg_type ==
|
|
- NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) {
|
|
- /* Copy the response back */
|
|
- memcpy(&net_device->channel_init_pkt, nvsp_packet,
|
|
- sizeof(struct nvsp_message));
|
|
- complete(&net_device->channel_init_wait);
|
|
- } else if (nvsp_packet->hdr.msg_type ==
|
|
- NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) {
|
|
- /* Get the send context */
|
|
- nvsc_packet = (struct hv_netvsc_packet *)(unsigned long)
|
|
- packet->trans_id;
|
|
-
|
|
- /* Notify the layer above us */
|
|
- nvsc_packet->completion.send.send_completion(
|
|
- nvsc_packet->completion.send.send_completion_ctx);
|
|
-
|
|
- atomic_dec(&net_device->num_outstanding_sends);
|
|
- } else {
|
|
- netdev_err(ndev, "Unknown send completion packet type- "
|
|
- "%d received!!\n", nvsp_packet->hdr.msg_type);
|
|
- }
|
|
-
|
|
-}
|
|
-
|
|
-int netvsc_send(struct hv_device *device,
|
|
- struct hv_netvsc_packet *packet)
|
|
-{
|
|
- struct netvsc_device *net_device;
|
|
- int ret = 0;
|
|
- struct nvsp_message sendMessage;
|
|
- struct net_device *ndev;
|
|
-
|
|
- net_device = get_outbound_net_device(device);
|
|
- if (!net_device)
|
|
- return -ENODEV;
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
|
|
- if (packet->is_data_pkt) {
|
|
- /* 0 is RMC_DATA; */
|
|
- sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 0;
|
|
- } else {
|
|
- /* 1 is RMC_CONTROL; */
|
|
- sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1;
|
|
- }
|
|
-
|
|
- /* Not using send buffer section */
|
|
- sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index =
|
|
- 0xFFFFFFFF;
|
|
- sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0;
|
|
-
|
|
- if (packet->page_buf_cnt) {
|
|
- ret = vmbus_sendpacket_pagebuffer(device->channel,
|
|
- packet->page_buf,
|
|
- packet->page_buf_cnt,
|
|
- &sendMessage,
|
|
- sizeof(struct nvsp_message),
|
|
- (unsigned long)packet);
|
|
- } else {
|
|
- ret = vmbus_sendpacket(device->channel, &sendMessage,
|
|
- sizeof(struct nvsp_message),
|
|
- (unsigned long)packet,
|
|
- VM_PKT_DATA_INBAND,
|
|
- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
|
-
|
|
- }
|
|
-
|
|
- if (ret != 0)
|
|
- netdev_err(ndev, "Unable to send packet %p ret %d\n",
|
|
- packet, ret);
|
|
- else
|
|
- atomic_inc(&net_device->num_outstanding_sends);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void netvsc_send_recv_completion(struct hv_device *device,
|
|
- u64 transaction_id)
|
|
-{
|
|
- struct nvsp_message recvcompMessage;
|
|
- int retries = 0;
|
|
- int ret;
|
|
- struct net_device *ndev;
|
|
- struct netvsc_device *net_device = hv_get_drvdata(device);
|
|
-
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- recvcompMessage.hdr.msg_type =
|
|
- NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE;
|
|
-
|
|
- /* FIXME: Pass in the status */
|
|
- recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status =
|
|
- NVSP_STAT_SUCCESS;
|
|
-
|
|
-retry_send_cmplt:
|
|
- /* Send the completion */
|
|
- ret = vmbus_sendpacket(device->channel, &recvcompMessage,
|
|
- sizeof(struct nvsp_message), transaction_id,
|
|
- VM_PKT_COMP, 0);
|
|
- if (ret == 0) {
|
|
- /* success */
|
|
- /* no-op */
|
|
- } else if (ret == -EAGAIN) {
|
|
- /* no more room...wait a bit and attempt to retry 3 times */
|
|
- retries++;
|
|
- netdev_err(ndev, "unable to send receive completion pkt"
|
|
- " (tid %llx)...retrying %d\n", transaction_id, retries);
|
|
-
|
|
- if (retries < 4) {
|
|
- udelay(100);
|
|
- goto retry_send_cmplt;
|
|
- } else {
|
|
- netdev_err(ndev, "unable to send receive "
|
|
- "completion pkt (tid %llx)...give up retrying\n",
|
|
- transaction_id);
|
|
- }
|
|
- } else {
|
|
- netdev_err(ndev, "unable to send receive "
|
|
- "completion pkt - %llx\n", transaction_id);
|
|
- }
|
|
-}
|
|
-
|
|
-/* Send a receive completion packet to RNDIS device (ie NetVsp) */
|
|
-static void netvsc_receive_completion(void *context)
|
|
-{
|
|
- struct hv_netvsc_packet *packet = context;
|
|
- struct hv_device *device = (struct hv_device *)packet->device;
|
|
- struct netvsc_device *net_device;
|
|
- u64 transaction_id = 0;
|
|
- bool fsend_receive_comp = false;
|
|
- unsigned long flags;
|
|
- struct net_device *ndev;
|
|
-
|
|
- /*
|
|
- * Even though it seems logical to do a GetOutboundNetDevice() here to
|
|
- * send out receive completion, we are using GetInboundNetDevice()
|
|
- * since we may have disable outbound traffic already.
|
|
- */
|
|
- net_device = get_inbound_net_device(device);
|
|
- if (!net_device)
|
|
- return;
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- /* Overloading use of the lock. */
|
|
- spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
|
|
-
|
|
- packet->xfer_page_pkt->count--;
|
|
-
|
|
- /*
|
|
- * Last one in the line that represent 1 xfer page packet.
|
|
- * Return the xfer page packet itself to the freelist
|
|
- */
|
|
- if (packet->xfer_page_pkt->count == 0) {
|
|
- fsend_receive_comp = true;
|
|
- transaction_id = packet->completion.recv.recv_completion_tid;
|
|
- list_add_tail(&packet->xfer_page_pkt->list_ent,
|
|
- &net_device->recv_pkt_list);
|
|
-
|
|
- }
|
|
-
|
|
- /* Put the packet back */
|
|
- list_add_tail(&packet->list_ent, &net_device->recv_pkt_list);
|
|
- spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
|
|
-
|
|
- /* Send a receive completion for the xfer page packet */
|
|
- if (fsend_receive_comp)
|
|
- netvsc_send_recv_completion(device, transaction_id);
|
|
-
|
|
-}
|
|
-
|
|
-static void netvsc_receive(struct hv_device *device,
|
|
- struct vmpacket_descriptor *packet)
|
|
-{
|
|
- struct netvsc_device *net_device;
|
|
- struct vmtransfer_page_packet_header *vmxferpage_packet;
|
|
- struct nvsp_message *nvsp_packet;
|
|
- struct hv_netvsc_packet *netvsc_packet = NULL;
|
|
- unsigned long start;
|
|
- unsigned long end, end_virtual;
|
|
- /* struct netvsc_driver *netvscDriver; */
|
|
- struct xferpage_packet *xferpage_packet = NULL;
|
|
- int i, j;
|
|
- int count = 0, bytes_remain = 0;
|
|
- unsigned long flags;
|
|
- struct net_device *ndev;
|
|
-
|
|
- LIST_HEAD(listHead);
|
|
-
|
|
- net_device = get_inbound_net_device(device);
|
|
- if (!net_device)
|
|
- return;
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- /*
|
|
- * All inbound packets other than send completion should be xfer page
|
|
- * packet
|
|
- */
|
|
- if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) {
|
|
- netdev_err(ndev, "Unknown packet type received - %d\n",
|
|
- packet->type);
|
|
- return;
|
|
- }
|
|
-
|
|
- nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
|
|
- (packet->offset8 << 3));
|
|
-
|
|
- /* Make sure this is a valid nvsp packet */
|
|
- if (nvsp_packet->hdr.msg_type !=
|
|
- NVSP_MSG1_TYPE_SEND_RNDIS_PKT) {
|
|
- netdev_err(ndev, "Unknown nvsp packet type received-"
|
|
- " %d\n", nvsp_packet->hdr.msg_type);
|
|
- return;
|
|
- }
|
|
-
|
|
- vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet;
|
|
-
|
|
- if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) {
|
|
- netdev_err(ndev, "Invalid xfer page set id - "
|
|
- "expecting %x got %x\n", NETVSC_RECEIVE_BUFFER_ID,
|
|
- vmxferpage_packet->xfer_pageset_id);
|
|
- return;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Grab free packets (range count + 1) to represent this xfer
|
|
- * page packet. +1 to represent the xfer page packet itself.
|
|
- * We grab it here so that we know exactly how many we can
|
|
- * fulfil
|
|
- */
|
|
- spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
|
|
- while (!list_empty(&net_device->recv_pkt_list)) {
|
|
- list_move_tail(net_device->recv_pkt_list.next, &listHead);
|
|
- if (++count == vmxferpage_packet->range_cnt + 1)
|
|
- break;
|
|
- }
|
|
- spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
|
|
-
|
|
- /*
|
|
- * We need at least 2 netvsc pkts (1 to represent the xfer
|
|
- * page and at least 1 for the range) i.e. we can handled
|
|
- * some of the xfer page packet ranges...
|
|
- */
|
|
- if (count < 2) {
|
|
- netdev_err(ndev, "Got only %d netvsc pkt...needed "
|
|
- "%d pkts. Dropping this xfer page packet completely!\n",
|
|
- count, vmxferpage_packet->range_cnt + 1);
|
|
-
|
|
- /* Return it to the freelist */
|
|
- spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
|
|
- for (i = count; i != 0; i--) {
|
|
- list_move_tail(listHead.next,
|
|
- &net_device->recv_pkt_list);
|
|
- }
|
|
- spin_unlock_irqrestore(&net_device->recv_pkt_list_lock,
|
|
- flags);
|
|
-
|
|
- netvsc_send_recv_completion(device,
|
|
- vmxferpage_packet->d.trans_id);
|
|
-
|
|
- return;
|
|
- }
|
|
-
|
|
- /* Remove the 1st packet to represent the xfer page packet itself */
|
|
- xferpage_packet = (struct xferpage_packet *)listHead.next;
|
|
- list_del(&xferpage_packet->list_ent);
|
|
-
|
|
- /* This is how much we can satisfy */
|
|
- xferpage_packet->count = count - 1;
|
|
-
|
|
- if (xferpage_packet->count != vmxferpage_packet->range_cnt) {
|
|
- netdev_err(ndev, "Needed %d netvsc pkts to satisfy "
|
|
- "this xfer page...got %d\n",
|
|
- vmxferpage_packet->range_cnt, xferpage_packet->count);
|
|
- }
|
|
-
|
|
- /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
|
|
- for (i = 0; i < (count - 1); i++) {
|
|
- netvsc_packet = (struct hv_netvsc_packet *)listHead.next;
|
|
- list_del(&netvsc_packet->list_ent);
|
|
-
|
|
- /* Initialize the netvsc packet */
|
|
- netvsc_packet->xfer_page_pkt = xferpage_packet;
|
|
- netvsc_packet->completion.recv.recv_completion =
|
|
- netvsc_receive_completion;
|
|
- netvsc_packet->completion.recv.recv_completion_ctx =
|
|
- netvsc_packet;
|
|
- netvsc_packet->device = device;
|
|
- /* Save this so that we can send it back */
|
|
- netvsc_packet->completion.recv.recv_completion_tid =
|
|
- vmxferpage_packet->d.trans_id;
|
|
-
|
|
- netvsc_packet->total_data_buflen =
|
|
- vmxferpage_packet->ranges[i].byte_count;
|
|
- netvsc_packet->page_buf_cnt = 1;
|
|
-
|
|
- netvsc_packet->page_buf[0].len =
|
|
- vmxferpage_packet->ranges[i].byte_count;
|
|
-
|
|
- start = virt_to_phys((void *)((unsigned long)net_device->
|
|
- recv_buf + vmxferpage_packet->ranges[i].byte_offset));
|
|
-
|
|
- netvsc_packet->page_buf[0].pfn = start >> PAGE_SHIFT;
|
|
- end_virtual = (unsigned long)net_device->recv_buf
|
|
- + vmxferpage_packet->ranges[i].byte_offset
|
|
- + vmxferpage_packet->ranges[i].byte_count - 1;
|
|
- end = virt_to_phys((void *)end_virtual);
|
|
-
|
|
- /* Calculate the page relative offset */
|
|
- netvsc_packet->page_buf[0].offset =
|
|
- vmxferpage_packet->ranges[i].byte_offset &
|
|
- (PAGE_SIZE - 1);
|
|
- if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) {
|
|
- /* Handle frame across multiple pages: */
|
|
- netvsc_packet->page_buf[0].len =
|
|
- (netvsc_packet->page_buf[0].pfn <<
|
|
- PAGE_SHIFT)
|
|
- + PAGE_SIZE - start;
|
|
- bytes_remain = netvsc_packet->total_data_buflen -
|
|
- netvsc_packet->page_buf[0].len;
|
|
- for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) {
|
|
- netvsc_packet->page_buf[j].offset = 0;
|
|
- if (bytes_remain <= PAGE_SIZE) {
|
|
- netvsc_packet->page_buf[j].len =
|
|
- bytes_remain;
|
|
- bytes_remain = 0;
|
|
- } else {
|
|
- netvsc_packet->page_buf[j].len =
|
|
- PAGE_SIZE;
|
|
- bytes_remain -= PAGE_SIZE;
|
|
- }
|
|
- netvsc_packet->page_buf[j].pfn =
|
|
- virt_to_phys((void *)(end_virtual -
|
|
- bytes_remain)) >> PAGE_SHIFT;
|
|
- netvsc_packet->page_buf_cnt++;
|
|
- if (bytes_remain == 0)
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Pass it to the upper layer */
|
|
- rndis_filter_receive(device, netvsc_packet);
|
|
-
|
|
- netvsc_receive_completion(netvsc_packet->
|
|
- completion.recv.recv_completion_ctx);
|
|
- }
|
|
-
|
|
-}
|
|
-
|
|
-static void netvsc_channel_cb(void *context)
|
|
-{
|
|
- int ret;
|
|
- struct hv_device *device = context;
|
|
- struct netvsc_device *net_device;
|
|
- u32 bytes_recvd;
|
|
- u64 request_id;
|
|
- unsigned char *packet;
|
|
- struct vmpacket_descriptor *desc;
|
|
- unsigned char *buffer;
|
|
- int bufferlen = NETVSC_PACKET_SIZE;
|
|
- struct net_device *ndev;
|
|
-
|
|
- packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char),
|
|
- GFP_ATOMIC);
|
|
- if (!packet)
|
|
- return;
|
|
- buffer = packet;
|
|
-
|
|
- net_device = get_inbound_net_device(device);
|
|
- if (!net_device)
|
|
- goto out;
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- do {
|
|
- ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen,
|
|
- &bytes_recvd, &request_id);
|
|
- if (ret == 0) {
|
|
- if (bytes_recvd > 0) {
|
|
- desc = (struct vmpacket_descriptor *)buffer;
|
|
- switch (desc->type) {
|
|
- case VM_PKT_COMP:
|
|
- netvsc_send_completion(device, desc);
|
|
- break;
|
|
-
|
|
- case VM_PKT_DATA_USING_XFER_PAGES:
|
|
- netvsc_receive(device, desc);
|
|
- break;
|
|
-
|
|
- default:
|
|
- netdev_err(ndev,
|
|
- "unhandled packet type %d, "
|
|
- "tid %llx len %d\n",
|
|
- desc->type, request_id,
|
|
- bytes_recvd);
|
|
- break;
|
|
- }
|
|
-
|
|
- /* reset */
|
|
- if (bufferlen > NETVSC_PACKET_SIZE) {
|
|
- kfree(buffer);
|
|
- buffer = packet;
|
|
- bufferlen = NETVSC_PACKET_SIZE;
|
|
- }
|
|
- } else {
|
|
- /* reset */
|
|
- if (bufferlen > NETVSC_PACKET_SIZE) {
|
|
- kfree(buffer);
|
|
- buffer = packet;
|
|
- bufferlen = NETVSC_PACKET_SIZE;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- } else if (ret == -ENOBUFS) {
|
|
- /* Handle large packet */
|
|
- buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
|
|
- if (buffer == NULL) {
|
|
- /* Try again next time around */
|
|
- netdev_err(ndev,
|
|
- "unable to allocate buffer of size "
|
|
- "(%d)!!\n", bytes_recvd);
|
|
- break;
|
|
- }
|
|
-
|
|
- bufferlen = bytes_recvd;
|
|
- }
|
|
- } while (1);
|
|
-
|
|
-out:
|
|
- kfree(buffer);
|
|
- return;
|
|
-}
|
|
-
|
|
-/*
|
|
- * netvsc_device_add - Callback when the device belonging to this
|
|
- * driver is added
|
|
- */
|
|
-int netvsc_device_add(struct hv_device *device, void *additional_info)
|
|
-{
|
|
- int ret = 0;
|
|
- int i;
|
|
- int ring_size =
|
|
- ((struct netvsc_device_info *)additional_info)->ring_size;
|
|
- struct netvsc_device *net_device;
|
|
- struct hv_netvsc_packet *packet, *pos;
|
|
- struct net_device *ndev;
|
|
-
|
|
- net_device = alloc_net_device(device);
|
|
- if (!net_device) {
|
|
- ret = -ENOMEM;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Coming into this function, struct net_device * is
|
|
- * registered as the driver private data.
|
|
- * In alloc_net_device(), we register struct netvsc_device *
|
|
- * as the driver private data and stash away struct net_device *
|
|
- * in struct netvsc_device *.
|
|
- */
|
|
- ndev = net_device->ndev;
|
|
-
|
|
- /* Initialize the NetVSC channel extension */
|
|
- net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
|
|
- spin_lock_init(&net_device->recv_pkt_list_lock);
|
|
-
|
|
- INIT_LIST_HEAD(&net_device->recv_pkt_list);
|
|
-
|
|
- for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) {
|
|
- packet = kzalloc(sizeof(struct hv_netvsc_packet) +
|
|
- (NETVSC_RECEIVE_SG_COUNT *
|
|
- sizeof(struct hv_page_buffer)), GFP_KERNEL);
|
|
- if (!packet)
|
|
- break;
|
|
-
|
|
- list_add_tail(&packet->list_ent,
|
|
- &net_device->recv_pkt_list);
|
|
- }
|
|
- init_completion(&net_device->channel_init_wait);
|
|
-
|
|
- /* Open the channel */
|
|
- ret = vmbus_open(device->channel, ring_size * PAGE_SIZE,
|
|
- ring_size * PAGE_SIZE, NULL, 0,
|
|
- netvsc_channel_cb, device);
|
|
-
|
|
- if (ret != 0) {
|
|
- netdev_err(ndev, "unable to open channel: %d\n", ret);
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /* Channel is opened */
|
|
- pr_info("hv_netvsc channel opened successfully\n");
|
|
-
|
|
- /* Connect with the NetVsp */
|
|
- ret = netvsc_connect_vsp(device);
|
|
- if (ret != 0) {
|
|
- netdev_err(ndev,
|
|
- "unable to connect to NetVSP - %d\n", ret);
|
|
- goto close;
|
|
- }
|
|
-
|
|
- return ret;
|
|
-
|
|
-close:
|
|
- /* Now, we can close the channel safely */
|
|
- vmbus_close(device->channel);
|
|
-
|
|
-cleanup:
|
|
-
|
|
- if (net_device) {
|
|
- list_for_each_entry_safe(packet, pos,
|
|
- &net_device->recv_pkt_list,
|
|
- list_ent) {
|
|
- list_del(&packet->list_ent);
|
|
- kfree(packet);
|
|
- }
|
|
-
|
|
- kfree(net_device);
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c
|
|
deleted file mode 100644
|
|
index 93b0e91..0000000
|
|
--- a/drivers/staging/hv/netvsc_drv.c
|
|
+++ /dev/null
|
|
@@ -1,456 +0,0 @@
|
|
-/*
|
|
- * Copyright (c) 2009, Microsoft Corporation.
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or modify it
|
|
- * under the terms and conditions of the GNU General Public License,
|
|
- * version 2, as published by the Free Software Foundation.
|
|
- *
|
|
- * This program is distributed in the hope it will be useful, but WITHOUT
|
|
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
- * more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License along with
|
|
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
- * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
- *
|
|
- * Authors:
|
|
- * Haiyang Zhang <haiyangz@microsoft.com>
|
|
- * Hank Janssen <hjanssen@microsoft.com>
|
|
- */
|
|
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
-
|
|
-#include <linux/init.h>
|
|
-#include <linux/atomic.h>
|
|
-#include <linux/module.h>
|
|
-#include <linux/highmem.h>
|
|
-#include <linux/device.h>
|
|
-#include <linux/io.h>
|
|
-#include <linux/delay.h>
|
|
-#include <linux/netdevice.h>
|
|
-#include <linux/inetdevice.h>
|
|
-#include <linux/etherdevice.h>
|
|
-#include <linux/skbuff.h>
|
|
-#include <linux/in.h>
|
|
-#include <linux/slab.h>
|
|
-#include <net/arp.h>
|
|
-#include <net/route.h>
|
|
-#include <net/sock.h>
|
|
-#include <net/pkt_sched.h>
|
|
-
|
|
-#include "hyperv_net.h"
|
|
-
|
|
-struct net_device_context {
|
|
- /* point back to our device context */
|
|
- struct hv_device *device_ctx;
|
|
- atomic_t avail;
|
|
- struct delayed_work dwork;
|
|
-};
|
|
-
|
|
-
|
|
-#define PACKET_PAGES_LOWATER 8
|
|
-/* Need this many pages to handle worst case fragmented packet */
|
|
-#define PACKET_PAGES_HIWATER (MAX_SKB_FRAGS + 2)
|
|
-
|
|
-static int ring_size = 128;
|
|
-module_param(ring_size, int, S_IRUGO);
|
|
-MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
|
|
-
|
|
-/* no-op so the netdev core doesn't return -EINVAL when modifying the the
|
|
- * multicast address list in SIOCADDMULTI. hv is setup to get all multicast
|
|
- * when it calls RndisFilterOnOpen() */
|
|
-static void netvsc_set_multicast_list(struct net_device *net)
|
|
-{
|
|
-}
|
|
-
|
|
-static int netvsc_open(struct net_device *net)
|
|
-{
|
|
- struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
- struct hv_device *device_obj = net_device_ctx->device_ctx;
|
|
- int ret = 0;
|
|
-
|
|
- /* Open up the device */
|
|
- ret = rndis_filter_open(device_obj);
|
|
- if (ret != 0) {
|
|
- netdev_err(net, "unable to open device (ret %d).\n", ret);
|
|
- return ret;
|
|
- }
|
|
-
|
|
- netif_start_queue(net);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int netvsc_close(struct net_device *net)
|
|
-{
|
|
- struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
- struct hv_device *device_obj = net_device_ctx->device_ctx;
|
|
- int ret;
|
|
-
|
|
- netif_stop_queue(net);
|
|
-
|
|
- ret = rndis_filter_close(device_obj);
|
|
- if (ret != 0)
|
|
- netdev_err(net, "unable to close device (ret %d).\n", ret);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void netvsc_xmit_completion(void *context)
|
|
-{
|
|
- struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
|
|
- struct sk_buff *skb = (struct sk_buff *)
|
|
- (unsigned long)packet->completion.send.send_completion_tid;
|
|
-
|
|
- kfree(packet);
|
|
-
|
|
- if (skb) {
|
|
- struct net_device *net = skb->dev;
|
|
- struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
- unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2;
|
|
-
|
|
- dev_kfree_skb_any(skb);
|
|
-
|
|
- atomic_add(num_pages, &net_device_ctx->avail);
|
|
- if (atomic_read(&net_device_ctx->avail) >=
|
|
- PACKET_PAGES_HIWATER)
|
|
- netif_wake_queue(net);
|
|
- }
|
|
-}
|
|
-
|
|
-static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
|
-{
|
|
- struct net_device_context *net_device_ctx = netdev_priv(net);
|
|
- struct hv_netvsc_packet *packet;
|
|
- int ret;
|
|
- unsigned int i, num_pages;
|
|
-
|
|
- /* Add 1 for skb->data and additional one for RNDIS */
|
|
- num_pages = skb_shinfo(skb)->nr_frags + 1 + 1;
|
|
- if (num_pages > atomic_read(&net_device_ctx->avail))
|
|
- return NETDEV_TX_BUSY;
|
|
-
|
|
- /* Allocate a netvsc packet based on # of frags. */
|
|
- packet = kzalloc(sizeof(struct hv_netvsc_packet) +
|
|
- (num_pages * sizeof(struct hv_page_buffer)) +
|
|
- sizeof(struct rndis_filter_packet), GFP_ATOMIC);
|
|
- if (!packet) {
|
|
- /* out of memory, drop packet */
|
|
- netdev_err(net, "unable to allocate hv_netvsc_packet\n");
|
|
-
|
|
- dev_kfree_skb(skb);
|
|
- net->stats.tx_dropped++;
|
|
- return NETDEV_TX_BUSY;
|
|
- }
|
|
-
|
|
- packet->extension = (void *)(unsigned long)packet +
|
|
- sizeof(struct hv_netvsc_packet) +
|
|
- (num_pages * sizeof(struct hv_page_buffer));
|
|
-
|
|
- /* Setup the rndis header */
|
|
- packet->page_buf_cnt = num_pages;
|
|
-
|
|
- /* Initialize it from the skb */
|
|
- packet->total_data_buflen = skb->len;
|
|
-
|
|
- /* Start filling in the page buffers starting after RNDIS buffer. */
|
|
- packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
|
|
- packet->page_buf[1].offset
|
|
- = (unsigned long)skb->data & (PAGE_SIZE - 1);
|
|
- packet->page_buf[1].len = skb_headlen(skb);
|
|
-
|
|
- /* Additional fragments are after SKB data */
|
|
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
|
- const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
|
|
-
|
|
- packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f));
|
|
- packet->page_buf[i+2].offset = f->page_offset;
|
|
- packet->page_buf[i+2].len = skb_frag_size(f);
|
|
- }
|
|
-
|
|
- /* Set the completion routine */
|
|
- packet->completion.send.send_completion = netvsc_xmit_completion;
|
|
- packet->completion.send.send_completion_ctx = packet;
|
|
- packet->completion.send.send_completion_tid = (unsigned long)skb;
|
|
-
|
|
- ret = rndis_filter_send(net_device_ctx->device_ctx,
|
|
- packet);
|
|
- if (ret == 0) {
|
|
- net->stats.tx_bytes += skb->len;
|
|
- net->stats.tx_packets++;
|
|
-
|
|
- atomic_sub(num_pages, &net_device_ctx->avail);
|
|
- if (atomic_read(&net_device_ctx->avail) < PACKET_PAGES_LOWATER)
|
|
- netif_stop_queue(net);
|
|
- } else {
|
|
- /* we are shutting down or bus overloaded, just drop packet */
|
|
- net->stats.tx_dropped++;
|
|
- kfree(packet);
|
|
- dev_kfree_skb_any(skb);
|
|
- }
|
|
-
|
|
- return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK;
|
|
-}
|
|
-
|
|
-/*
|
|
- * netvsc_linkstatus_callback - Link up/down notification
|
|
- */
|
|
-void netvsc_linkstatus_callback(struct hv_device *device_obj,
|
|
- unsigned int status)
|
|
-{
|
|
- struct net_device *net;
|
|
- struct net_device_context *ndev_ctx;
|
|
- struct netvsc_device *net_device;
|
|
-
|
|
- net_device = hv_get_drvdata(device_obj);
|
|
- net = net_device->ndev;
|
|
-
|
|
- if (!net) {
|
|
- netdev_err(net, "got link status but net device "
|
|
- "not initialized yet\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- if (status == 1) {
|
|
- netif_carrier_on(net);
|
|
- netif_wake_queue(net);
|
|
- ndev_ctx = netdev_priv(net);
|
|
- schedule_delayed_work(&ndev_ctx->dwork, 0);
|
|
- schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));
|
|
- } else {
|
|
- netif_carrier_off(net);
|
|
- netif_stop_queue(net);
|
|
- }
|
|
-}
|
|
-
|
|
-/*
|
|
- * netvsc_recv_callback - Callback when we receive a packet from the
|
|
- * "wire" on the specified device.
|
|
- */
|
|
-int netvsc_recv_callback(struct hv_device *device_obj,
|
|
- struct hv_netvsc_packet *packet)
|
|
-{
|
|
- struct net_device *net = dev_get_drvdata(&device_obj->device);
|
|
- struct sk_buff *skb;
|
|
- void *data;
|
|
- int i;
|
|
- unsigned long flags;
|
|
- struct netvsc_device *net_device;
|
|
-
|
|
- net_device = hv_get_drvdata(device_obj);
|
|
- net = net_device->ndev;
|
|
-
|
|
- if (!net) {
|
|
- netdev_err(net, "got receive callback but net device"
|
|
- " not initialized yet\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /* Allocate a skb - TODO direct I/O to pages? */
|
|
- skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen);
|
|
- if (unlikely(!skb)) {
|
|
- ++net->stats.rx_dropped;
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /* for kmap_atomic */
|
|
- local_irq_save(flags);
|
|
-
|
|
- /*
|
|
- * Copy to skb. This copy is needed here since the memory pointed by
|
|
- * hv_netvsc_packet cannot be deallocated
|
|
- */
|
|
- for (i = 0; i < packet->page_buf_cnt; i++) {
|
|
- data = kmap_atomic(pfn_to_page(packet->page_buf[i].pfn),
|
|
- KM_IRQ1);
|
|
- data = (void *)(unsigned long)data +
|
|
- packet->page_buf[i].offset;
|
|
-
|
|
- memcpy(skb_put(skb, packet->page_buf[i].len), data,
|
|
- packet->page_buf[i].len);
|
|
-
|
|
- kunmap_atomic((void *)((unsigned long)data -
|
|
- packet->page_buf[i].offset), KM_IRQ1);
|
|
- }
|
|
-
|
|
- local_irq_restore(flags);
|
|
-
|
|
- skb->protocol = eth_type_trans(skb, net);
|
|
- skb->ip_summed = CHECKSUM_NONE;
|
|
-
|
|
- net->stats.rx_packets++;
|
|
- net->stats.rx_bytes += skb->len;
|
|
-
|
|
- /*
|
|
- * Pass the skb back up. Network stack will deallocate the skb when it
|
|
- * is done.
|
|
- * TODO - use NAPI?
|
|
- */
|
|
- netif_rx(skb);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void netvsc_get_drvinfo(struct net_device *net,
|
|
- struct ethtool_drvinfo *info)
|
|
-{
|
|
- strcpy(info->driver, "hv_netvsc");
|
|
- strcpy(info->version, HV_DRV_VERSION);
|
|
- strcpy(info->fw_version, "N/A");
|
|
-}
|
|
-
|
|
-static const struct ethtool_ops ethtool_ops = {
|
|
- .get_drvinfo = netvsc_get_drvinfo,
|
|
- .get_link = ethtool_op_get_link,
|
|
-};
|
|
-
|
|
-static const struct net_device_ops device_ops = {
|
|
- .ndo_open = netvsc_open,
|
|
- .ndo_stop = netvsc_close,
|
|
- .ndo_start_xmit = netvsc_start_xmit,
|
|
- .ndo_set_rx_mode = netvsc_set_multicast_list,
|
|
- .ndo_change_mtu = eth_change_mtu,
|
|
- .ndo_validate_addr = eth_validate_addr,
|
|
- .ndo_set_mac_address = eth_mac_addr,
|
|
-};
|
|
-
|
|
-/*
|
|
- * Send GARP packet to network peers after migrations.
|
|
- * After Quick Migration, the network is not immediately operational in the
|
|
- * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add
|
|
- * another netif_notify_peers() into a delayed work, otherwise GARP packet
|
|
- * will not be sent after quick migration, and cause network disconnection.
|
|
- */
|
|
-static void netvsc_send_garp(struct work_struct *w)
|
|
-{
|
|
- struct net_device_context *ndev_ctx;
|
|
- struct net_device *net;
|
|
- struct netvsc_device *net_device;
|
|
-
|
|
- ndev_ctx = container_of(w, struct net_device_context, dwork.work);
|
|
- net_device = hv_get_drvdata(ndev_ctx->device_ctx);
|
|
- net = net_device->ndev;
|
|
- netif_notify_peers(net);
|
|
-}
|
|
-
|
|
-
|
|
-static int netvsc_probe(struct hv_device *dev,
|
|
- const struct hv_vmbus_device_id *dev_id)
|
|
-{
|
|
- struct net_device *net = NULL;
|
|
- struct net_device_context *net_device_ctx;
|
|
- struct netvsc_device_info device_info;
|
|
- int ret;
|
|
-
|
|
- net = alloc_etherdev(sizeof(struct net_device_context));
|
|
- if (!net)
|
|
- return -ENOMEM;
|
|
-
|
|
- /* Set initial state */
|
|
- netif_carrier_off(net);
|
|
-
|
|
- net_device_ctx = netdev_priv(net);
|
|
- net_device_ctx->device_ctx = dev;
|
|
- atomic_set(&net_device_ctx->avail, ring_size);
|
|
- hv_set_drvdata(dev, net);
|
|
- INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp);
|
|
-
|
|
- net->netdev_ops = &device_ops;
|
|
-
|
|
- /* TODO: Add GSO and Checksum offload */
|
|
- net->hw_features = NETIF_F_SG;
|
|
- net->features = NETIF_F_SG;
|
|
-
|
|
- SET_ETHTOOL_OPS(net, ðtool_ops);
|
|
- SET_NETDEV_DEV(net, &dev->device);
|
|
-
|
|
- ret = register_netdev(net);
|
|
- if (ret != 0) {
|
|
- pr_err("Unable to register netdev.\n");
|
|
- free_netdev(net);
|
|
- goto out;
|
|
- }
|
|
-
|
|
- /* Notify the netvsc driver of the new device */
|
|
- device_info.ring_size = ring_size;
|
|
- ret = rndis_filter_device_add(dev, &device_info);
|
|
- if (ret != 0) {
|
|
- netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
|
|
- unregister_netdev(net);
|
|
- free_netdev(net);
|
|
- hv_set_drvdata(dev, NULL);
|
|
- return ret;
|
|
- }
|
|
- memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
|
|
-
|
|
- netif_carrier_on(net);
|
|
-
|
|
-out:
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int netvsc_remove(struct hv_device *dev)
|
|
-{
|
|
- struct net_device *net;
|
|
- struct net_device_context *ndev_ctx;
|
|
- struct netvsc_device *net_device;
|
|
-
|
|
- net_device = hv_get_drvdata(dev);
|
|
- net = net_device->ndev;
|
|
-
|
|
- if (net == NULL) {
|
|
- dev_err(&dev->device, "No net device to remove\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- ndev_ctx = netdev_priv(net);
|
|
- cancel_delayed_work_sync(&ndev_ctx->dwork);
|
|
-
|
|
- /* Stop outbound asap */
|
|
- netif_stop_queue(net);
|
|
-
|
|
- unregister_netdev(net);
|
|
-
|
|
- /*
|
|
- * Call to the vsc driver to let it know that the device is being
|
|
- * removed
|
|
- */
|
|
- rndis_filter_device_remove(dev);
|
|
-
|
|
- free_netdev(net);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static const struct hv_vmbus_device_id id_table[] = {
|
|
- /* Network guid */
|
|
- { VMBUS_DEVICE(0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
|
|
- 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E) },
|
|
- { },
|
|
-};
|
|
-
|
|
-MODULE_DEVICE_TABLE(vmbus, id_table);
|
|
-
|
|
-/* The one and only one */
|
|
-static struct hv_driver netvsc_drv = {
|
|
- .name = "netvsc",
|
|
- .id_table = id_table,
|
|
- .probe = netvsc_probe,
|
|
- .remove = netvsc_remove,
|
|
-};
|
|
-
|
|
-static void __exit netvsc_drv_exit(void)
|
|
-{
|
|
- vmbus_driver_unregister(&netvsc_drv);
|
|
-}
|
|
-
|
|
-static int __init netvsc_drv_init(void)
|
|
-{
|
|
- return vmbus_driver_register(&netvsc_drv);
|
|
-}
|
|
-
|
|
-MODULE_LICENSE("GPL");
|
|
-MODULE_VERSION(HV_DRV_VERSION);
|
|
-MODULE_DESCRIPTION("Microsoft Hyper-V network driver");
|
|
-
|
|
-module_init(netvsc_drv_init);
|
|
-module_exit(netvsc_drv_exit);
|
|
diff --git a/drivers/staging/hv/rndis_filter.c b/drivers/staging/hv/rndis_filter.c
|
|
deleted file mode 100644
|
|
index bafccb3..0000000
|
|
--- a/drivers/staging/hv/rndis_filter.c
|
|
+++ /dev/null
|
|
@@ -1,855 +0,0 @@
|
|
-/*
|
|
- * Copyright (c) 2009, Microsoft Corporation.
|
|
- *
|
|
- * This program is free software; you can redistribute it and/or modify it
|
|
- * under the terms and conditions of the GNU General Public License,
|
|
- * version 2, as published by the Free Software Foundation.
|
|
- *
|
|
- * This program is distributed in the hope it will be useful, but WITHOUT
|
|
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
- * more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License along with
|
|
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
- * Place - Suite 330, Boston, MA 02111-1307 USA.
|
|
- *
|
|
- * Authors:
|
|
- * Haiyang Zhang <haiyangz@microsoft.com>
|
|
- * Hank Janssen <hjanssen@microsoft.com>
|
|
- */
|
|
-#include <linux/kernel.h>
|
|
-#include <linux/sched.h>
|
|
-#include <linux/wait.h>
|
|
-#include <linux/highmem.h>
|
|
-#include <linux/slab.h>
|
|
-#include <linux/io.h>
|
|
-#include <linux/if_ether.h>
|
|
-#include <linux/netdevice.h>
|
|
-
|
|
-#include "hyperv_net.h"
|
|
-
|
|
-
|
|
-enum rndis_device_state {
|
|
- RNDIS_DEV_UNINITIALIZED = 0,
|
|
- RNDIS_DEV_INITIALIZING,
|
|
- RNDIS_DEV_INITIALIZED,
|
|
- RNDIS_DEV_DATAINITIALIZED,
|
|
-};
|
|
-
|
|
-struct rndis_device {
|
|
- struct netvsc_device *net_dev;
|
|
-
|
|
- enum rndis_device_state state;
|
|
- bool link_state;
|
|
- atomic_t new_req_id;
|
|
-
|
|
- spinlock_t request_lock;
|
|
- struct list_head req_list;
|
|
-
|
|
- unsigned char hw_mac_adr[ETH_ALEN];
|
|
-};
|
|
-
|
|
-struct rndis_request {
|
|
- struct list_head list_ent;
|
|
- struct completion wait_event;
|
|
-
|
|
- /*
|
|
- * FIXME: We assumed a fixed size response here. If we do ever need to
|
|
- * handle a bigger response, we can either define a max response
|
|
- * message or add a response buffer variable above this field
|
|
- */
|
|
- struct rndis_message response_msg;
|
|
-
|
|
- /* Simplify allocation by having a netvsc packet inline */
|
|
- struct hv_netvsc_packet pkt;
|
|
- struct hv_page_buffer buf;
|
|
- /* FIXME: We assumed a fixed size request here. */
|
|
- struct rndis_message request_msg;
|
|
-};
|
|
-
|
|
-static void rndis_filter_send_completion(void *ctx);
|
|
-
|
|
-static void rndis_filter_send_request_completion(void *ctx);
|
|
-
|
|
-
|
|
-
|
|
-static struct rndis_device *get_rndis_device(void)
|
|
-{
|
|
- struct rndis_device *device;
|
|
-
|
|
- device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
|
|
- if (!device)
|
|
- return NULL;
|
|
-
|
|
- spin_lock_init(&device->request_lock);
|
|
-
|
|
- INIT_LIST_HEAD(&device->req_list);
|
|
-
|
|
- device->state = RNDIS_DEV_UNINITIALIZED;
|
|
-
|
|
- return device;
|
|
-}
|
|
-
|
|
-static struct rndis_request *get_rndis_request(struct rndis_device *dev,
|
|
- u32 msg_type,
|
|
- u32 msg_len)
|
|
-{
|
|
- struct rndis_request *request;
|
|
- struct rndis_message *rndis_msg;
|
|
- struct rndis_set_request *set;
|
|
- unsigned long flags;
|
|
-
|
|
- request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
|
|
- if (!request)
|
|
- return NULL;
|
|
-
|
|
- init_completion(&request->wait_event);
|
|
-
|
|
- rndis_msg = &request->request_msg;
|
|
- rndis_msg->ndis_msg_type = msg_type;
|
|
- rndis_msg->msg_len = msg_len;
|
|
-
|
|
- /*
|
|
- * Set the request id. This field is always after the rndis header for
|
|
- * request/response packet types so we just used the SetRequest as a
|
|
- * template
|
|
- */
|
|
- set = &rndis_msg->msg.set_req;
|
|
- set->req_id = atomic_inc_return(&dev->new_req_id);
|
|
-
|
|
- /* Add to the request list */
|
|
- spin_lock_irqsave(&dev->request_lock, flags);
|
|
- list_add_tail(&request->list_ent, &dev->req_list);
|
|
- spin_unlock_irqrestore(&dev->request_lock, flags);
|
|
-
|
|
- return request;
|
|
-}
|
|
-
|
|
-static void put_rndis_request(struct rndis_device *dev,
|
|
- struct rndis_request *req)
|
|
-{
|
|
- unsigned long flags;
|
|
-
|
|
- spin_lock_irqsave(&dev->request_lock, flags);
|
|
- list_del(&req->list_ent);
|
|
- spin_unlock_irqrestore(&dev->request_lock, flags);
|
|
-
|
|
- kfree(req);
|
|
-}
|
|
-
|
|
-static void dump_rndis_message(struct hv_device *hv_dev,
|
|
- struct rndis_message *rndis_msg)
|
|
-{
|
|
- struct net_device *netdev;
|
|
- struct netvsc_device *net_device;
|
|
-
|
|
- net_device = hv_get_drvdata(hv_dev);
|
|
- netdev = net_device->ndev;
|
|
-
|
|
- switch (rndis_msg->ndis_msg_type) {
|
|
- case REMOTE_NDIS_PACKET_MSG:
|
|
- netdev_dbg(netdev, "REMOTE_NDIS_PACKET_MSG (len %u, "
|
|
- "data offset %u data len %u, # oob %u, "
|
|
- "oob offset %u, oob len %u, pkt offset %u, "
|
|
- "pkt len %u\n",
|
|
- rndis_msg->msg_len,
|
|
- rndis_msg->msg.pkt.data_offset,
|
|
- rndis_msg->msg.pkt.data_len,
|
|
- rndis_msg->msg.pkt.num_oob_data_elements,
|
|
- rndis_msg->msg.pkt.oob_data_offset,
|
|
- rndis_msg->msg.pkt.oob_data_len,
|
|
- rndis_msg->msg.pkt.per_pkt_info_offset,
|
|
- rndis_msg->msg.pkt.per_pkt_info_len);
|
|
- break;
|
|
-
|
|
- case REMOTE_NDIS_INITIALIZE_CMPLT:
|
|
- netdev_dbg(netdev, "REMOTE_NDIS_INITIALIZE_CMPLT "
|
|
- "(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
|
|
- "device flags %d, max xfer size 0x%x, max pkts %u, "
|
|
- "pkt aligned %u)\n",
|
|
- rndis_msg->msg_len,
|
|
- rndis_msg->msg.init_complete.req_id,
|
|
- rndis_msg->msg.init_complete.status,
|
|
- rndis_msg->msg.init_complete.major_ver,
|
|
- rndis_msg->msg.init_complete.minor_ver,
|
|
- rndis_msg->msg.init_complete.dev_flags,
|
|
- rndis_msg->msg.init_complete.max_xfer_size,
|
|
- rndis_msg->msg.init_complete.
|
|
- max_pkt_per_msg,
|
|
- rndis_msg->msg.init_complete.
|
|
- pkt_alignment_factor);
|
|
- break;
|
|
-
|
|
- case REMOTE_NDIS_QUERY_CMPLT:
|
|
- netdev_dbg(netdev, "REMOTE_NDIS_QUERY_CMPLT "
|
|
- "(len %u, id 0x%x, status 0x%x, buf len %u, "
|
|
- "buf offset %u)\n",
|
|
- rndis_msg->msg_len,
|
|
- rndis_msg->msg.query_complete.req_id,
|
|
- rndis_msg->msg.query_complete.status,
|
|
- rndis_msg->msg.query_complete.
|
|
- info_buflen,
|
|
- rndis_msg->msg.query_complete.
|
|
- info_buf_offset);
|
|
- break;
|
|
-
|
|
- case REMOTE_NDIS_SET_CMPLT:
|
|
- netdev_dbg(netdev,
|
|
- "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)\n",
|
|
- rndis_msg->msg_len,
|
|
- rndis_msg->msg.set_complete.req_id,
|
|
- rndis_msg->msg.set_complete.status);
|
|
- break;
|
|
-
|
|
- case REMOTE_NDIS_INDICATE_STATUS_MSG:
|
|
- netdev_dbg(netdev, "REMOTE_NDIS_INDICATE_STATUS_MSG "
|
|
- "(len %u, status 0x%x, buf len %u, buf offset %u)\n",
|
|
- rndis_msg->msg_len,
|
|
- rndis_msg->msg.indicate_status.status,
|
|
- rndis_msg->msg.indicate_status.status_buflen,
|
|
- rndis_msg->msg.indicate_status.status_buf_offset);
|
|
- break;
|
|
-
|
|
- default:
|
|
- netdev_dbg(netdev, "0x%x (len %u)\n",
|
|
- rndis_msg->ndis_msg_type,
|
|
- rndis_msg->msg_len);
|
|
- break;
|
|
- }
|
|
-}
|
|
-
|
|
-static int rndis_filter_send_request(struct rndis_device *dev,
|
|
- struct rndis_request *req)
|
|
-{
|
|
- int ret;
|
|
- struct hv_netvsc_packet *packet;
|
|
-
|
|
- /* Setup the packet to send it */
|
|
- packet = &req->pkt;
|
|
-
|
|
- packet->is_data_pkt = false;
|
|
- packet->total_data_buflen = req->request_msg.msg_len;
|
|
- packet->page_buf_cnt = 1;
|
|
-
|
|
- packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >>
|
|
- PAGE_SHIFT;
|
|
- packet->page_buf[0].len = req->request_msg.msg_len;
|
|
- packet->page_buf[0].offset =
|
|
- (unsigned long)&req->request_msg & (PAGE_SIZE - 1);
|
|
-
|
|
- packet->completion.send.send_completion_ctx = req;/* packet; */
|
|
- packet->completion.send.send_completion =
|
|
- rndis_filter_send_request_completion;
|
|
- packet->completion.send.send_completion_tid = (unsigned long)dev;
|
|
-
|
|
- ret = netvsc_send(dev->net_dev->dev, packet);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void rndis_filter_receive_response(struct rndis_device *dev,
|
|
- struct rndis_message *resp)
|
|
-{
|
|
- struct rndis_request *request = NULL;
|
|
- bool found = false;
|
|
- unsigned long flags;
|
|
- struct net_device *ndev;
|
|
-
|
|
- ndev = dev->net_dev->ndev;
|
|
-
|
|
- spin_lock_irqsave(&dev->request_lock, flags);
|
|
- list_for_each_entry(request, &dev->req_list, list_ent) {
|
|
- /*
|
|
- * All request/response message contains RequestId as the 1st
|
|
- * field
|
|
- */
|
|
- if (request->request_msg.msg.init_req.req_id
|
|
- == resp->msg.init_complete.req_id) {
|
|
- found = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
- spin_unlock_irqrestore(&dev->request_lock, flags);
|
|
-
|
|
- if (found) {
|
|
- if (resp->msg_len <= sizeof(struct rndis_message)) {
|
|
- memcpy(&request->response_msg, resp,
|
|
- resp->msg_len);
|
|
- } else {
|
|
- netdev_err(ndev,
|
|
- "rndis response buffer overflow "
|
|
- "detected (size %u max %zu)\n",
|
|
- resp->msg_len,
|
|
- sizeof(struct rndis_filter_packet));
|
|
-
|
|
- if (resp->ndis_msg_type ==
|
|
- REMOTE_NDIS_RESET_CMPLT) {
|
|
- /* does not have a request id field */
|
|
- request->response_msg.msg.reset_complete.
|
|
- status = STATUS_BUFFER_OVERFLOW;
|
|
- } else {
|
|
- request->response_msg.msg.
|
|
- init_complete.status =
|
|
- STATUS_BUFFER_OVERFLOW;
|
|
- }
|
|
- }
|
|
-
|
|
- complete(&request->wait_event);
|
|
- } else {
|
|
- netdev_err(ndev,
|
|
- "no rndis request found for this response "
|
|
- "(id 0x%x res type 0x%x)\n",
|
|
- resp->msg.init_complete.req_id,
|
|
- resp->ndis_msg_type);
|
|
- }
|
|
-}
|
|
-
|
|
-static void rndis_filter_receive_indicate_status(struct rndis_device *dev,
|
|
- struct rndis_message *resp)
|
|
-{
|
|
- struct rndis_indicate_status *indicate =
|
|
- &resp->msg.indicate_status;
|
|
-
|
|
- if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) {
|
|
- netvsc_linkstatus_callback(
|
|
- dev->net_dev->dev, 1);
|
|
- } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) {
|
|
- netvsc_linkstatus_callback(
|
|
- dev->net_dev->dev, 0);
|
|
- } else {
|
|
- /*
|
|
- * TODO:
|
|
- */
|
|
- }
|
|
-}
|
|
-
|
|
-static void rndis_filter_receive_data(struct rndis_device *dev,
|
|
- struct rndis_message *msg,
|
|
- struct hv_netvsc_packet *pkt)
|
|
-{
|
|
- struct rndis_packet *rndis_pkt;
|
|
- u32 data_offset;
|
|
- int i;
|
|
-
|
|
- rndis_pkt = &msg->msg.pkt;
|
|
-
|
|
- /*
|
|
- * FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this
|
|
- * netvsc packet (ie TotalDataBufferLength != MessageLength)
|
|
- */
|
|
-
|
|
- /* Remove the rndis header and pass it back up the stack */
|
|
- data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
|
|
-
|
|
- pkt->total_data_buflen -= data_offset;
|
|
- pkt->page_buf[0].offset += data_offset;
|
|
- pkt->page_buf[0].len -= data_offset;
|
|
-
|
|
- /* Drop the 0th page, if rndis data go beyond page boundary */
|
|
- if (pkt->page_buf[0].offset >= PAGE_SIZE) {
|
|
- pkt->page_buf[1].offset = pkt->page_buf[0].offset - PAGE_SIZE;
|
|
- pkt->page_buf[1].len -= pkt->page_buf[1].offset;
|
|
- pkt->page_buf_cnt--;
|
|
- for (i = 0; i < pkt->page_buf_cnt; i++)
|
|
- pkt->page_buf[i] = pkt->page_buf[i+1];
|
|
- }
|
|
-
|
|
- pkt->is_data_pkt = true;
|
|
-
|
|
- netvsc_recv_callback(dev->net_dev->dev, pkt);
|
|
-}
|
|
-
|
|
-int rndis_filter_receive(struct hv_device *dev,
|
|
- struct hv_netvsc_packet *pkt)
|
|
-{
|
|
- struct netvsc_device *net_dev = hv_get_drvdata(dev);
|
|
- struct rndis_device *rndis_dev;
|
|
- struct rndis_message rndis_msg;
|
|
- struct rndis_message *rndis_hdr;
|
|
- struct net_device *ndev;
|
|
-
|
|
- if (!net_dev)
|
|
- return -EINVAL;
|
|
-
|
|
- ndev = net_dev->ndev;
|
|
-
|
|
- /* Make sure the rndis device state is initialized */
|
|
- if (!net_dev->extension) {
|
|
- netdev_err(ndev, "got rndis message but no rndis device - "
|
|
- "dropping this message!\n");
|
|
- return -ENODEV;
|
|
- }
|
|
-
|
|
- rndis_dev = (struct rndis_device *)net_dev->extension;
|
|
- if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
|
|
- netdev_err(ndev, "got rndis message but rndis device "
|
|
- "uninitialized...dropping this message!\n");
|
|
- return -ENODEV;
|
|
- }
|
|
-
|
|
- rndis_hdr = (struct rndis_message *)kmap_atomic(
|
|
- pfn_to_page(pkt->page_buf[0].pfn), KM_IRQ0);
|
|
-
|
|
- rndis_hdr = (void *)((unsigned long)rndis_hdr +
|
|
- pkt->page_buf[0].offset);
|
|
-
|
|
- /* Make sure we got a valid rndis message */
|
|
- if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) &&
|
|
- (rndis_hdr->msg_len > sizeof(struct rndis_message))) {
|
|
- netdev_err(ndev, "incoming rndis message buffer overflow "
|
|
- "detected (got %u, max %zu)..marking it an error!\n",
|
|
- rndis_hdr->msg_len,
|
|
- sizeof(struct rndis_message));
|
|
- }
|
|
-
|
|
- memcpy(&rndis_msg, rndis_hdr,
|
|
- (rndis_hdr->msg_len > sizeof(struct rndis_message)) ?
|
|
- sizeof(struct rndis_message) :
|
|
- rndis_hdr->msg_len);
|
|
-
|
|
- kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0);
|
|
-
|
|
- dump_rndis_message(dev, &rndis_msg);
|
|
-
|
|
- switch (rndis_msg.ndis_msg_type) {
|
|
- case REMOTE_NDIS_PACKET_MSG:
|
|
- /* data msg */
|
|
- rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt);
|
|
- break;
|
|
-
|
|
- case REMOTE_NDIS_INITIALIZE_CMPLT:
|
|
- case REMOTE_NDIS_QUERY_CMPLT:
|
|
- case REMOTE_NDIS_SET_CMPLT:
|
|
- /* completion msgs */
|
|
- rndis_filter_receive_response(rndis_dev, &rndis_msg);
|
|
- break;
|
|
-
|
|
- case REMOTE_NDIS_INDICATE_STATUS_MSG:
|
|
- /* notification msgs */
|
|
- rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg);
|
|
- break;
|
|
- default:
|
|
- netdev_err(ndev,
|
|
- "unhandled rndis message (type %u len %u)\n",
|
|
- rndis_msg.ndis_msg_type,
|
|
- rndis_msg.msg_len);
|
|
- break;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
|
|
- void *result, u32 *result_size)
|
|
-{
|
|
- struct rndis_request *request;
|
|
- u32 inresult_size = *result_size;
|
|
- struct rndis_query_request *query;
|
|
- struct rndis_query_complete *query_complete;
|
|
- int ret = 0;
|
|
- int t;
|
|
-
|
|
- if (!result)
|
|
- return -EINVAL;
|
|
-
|
|
- *result_size = 0;
|
|
- request = get_rndis_request(dev, REMOTE_NDIS_QUERY_MSG,
|
|
- RNDIS_MESSAGE_SIZE(struct rndis_query_request));
|
|
- if (!request) {
|
|
- ret = -ENOMEM;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /* Setup the rndis query */
|
|
- query = &request->request_msg.msg.query_req;
|
|
- query->oid = oid;
|
|
- query->info_buf_offset = sizeof(struct rndis_query_request);
|
|
- query->info_buflen = 0;
|
|
- query->dev_vc_handle = 0;
|
|
-
|
|
- ret = rndis_filter_send_request(dev, request);
|
|
- if (ret != 0)
|
|
- goto cleanup;
|
|
-
|
|
- t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
|
|
- if (t == 0) {
|
|
- ret = -ETIMEDOUT;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /* Copy the response back */
|
|
- query_complete = &request->response_msg.msg.query_complete;
|
|
-
|
|
- if (query_complete->info_buflen > inresult_size) {
|
|
- ret = -1;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- memcpy(result,
|
|
- (void *)((unsigned long)query_complete +
|
|
- query_complete->info_buf_offset),
|
|
- query_complete->info_buflen);
|
|
-
|
|
- *result_size = query_complete->info_buflen;
|
|
-
|
|
-cleanup:
|
|
- if (request)
|
|
- put_rndis_request(dev, request);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int rndis_filter_query_device_mac(struct rndis_device *dev)
|
|
-{
|
|
- u32 size = ETH_ALEN;
|
|
-
|
|
- return rndis_filter_query_device(dev,
|
|
- RNDIS_OID_802_3_PERMANENT_ADDRESS,
|
|
- dev->hw_mac_adr, &size);
|
|
-}
|
|
-
|
|
-static int rndis_filter_query_device_link_status(struct rndis_device *dev)
|
|
-{
|
|
- u32 size = sizeof(u32);
|
|
- u32 link_status;
|
|
- int ret;
|
|
-
|
|
- ret = rndis_filter_query_device(dev,
|
|
- RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
|
|
- &link_status, &size);
|
|
- dev->link_state = (link_status != 0) ? true : false;
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int rndis_filter_set_packet_filter(struct rndis_device *dev,
|
|
- u32 new_filter)
|
|
-{
|
|
- struct rndis_request *request;
|
|
- struct rndis_set_request *set;
|
|
- struct rndis_set_complete *set_complete;
|
|
- u32 status;
|
|
- int ret, t;
|
|
- struct net_device *ndev;
|
|
-
|
|
- ndev = dev->net_dev->ndev;
|
|
-
|
|
- request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG,
|
|
- RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
|
|
- sizeof(u32));
|
|
- if (!request) {
|
|
- ret = -ENOMEM;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /* Setup the rndis set */
|
|
- set = &request->request_msg.msg.set_req;
|
|
- set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
|
|
- set->info_buflen = sizeof(u32);
|
|
- set->info_buf_offset = sizeof(struct rndis_set_request);
|
|
-
|
|
- memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
|
|
- &new_filter, sizeof(u32));
|
|
-
|
|
- ret = rndis_filter_send_request(dev, request);
|
|
- if (ret != 0)
|
|
- goto cleanup;
|
|
-
|
|
- t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
|
|
-
|
|
- if (t == 0) {
|
|
- netdev_err(ndev,
|
|
- "timeout before we got a set response...\n");
|
|
- /*
|
|
- * We can't deallocate the request since we may still receive a
|
|
- * send completion for it.
|
|
- */
|
|
- goto exit;
|
|
- } else {
|
|
- set_complete = &request->response_msg.msg.set_complete;
|
|
- status = set_complete->status;
|
|
- }
|
|
-
|
|
-cleanup:
|
|
- if (request)
|
|
- put_rndis_request(dev, request);
|
|
-exit:
|
|
- return ret;
|
|
-}
|
|
-
|
|
-
|
|
-static int rndis_filter_init_device(struct rndis_device *dev)
|
|
-{
|
|
- struct rndis_request *request;
|
|
- struct rndis_initialize_request *init;
|
|
- struct rndis_initialize_complete *init_complete;
|
|
- u32 status;
|
|
- int ret, t;
|
|
-
|
|
- request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG,
|
|
- RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
|
|
- if (!request) {
|
|
- ret = -ENOMEM;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- /* Setup the rndis set */
|
|
- init = &request->request_msg.msg.init_req;
|
|
- init->major_ver = RNDIS_MAJOR_VERSION;
|
|
- init->minor_ver = RNDIS_MINOR_VERSION;
|
|
- /* FIXME: Use 1536 - rounded ethernet frame size */
|
|
- init->max_xfer_size = 2048;
|
|
-
|
|
- dev->state = RNDIS_DEV_INITIALIZING;
|
|
-
|
|
- ret = rndis_filter_send_request(dev, request);
|
|
- if (ret != 0) {
|
|
- dev->state = RNDIS_DEV_UNINITIALIZED;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
-
|
|
- t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
|
|
-
|
|
- if (t == 0) {
|
|
- ret = -ETIMEDOUT;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- init_complete = &request->response_msg.msg.init_complete;
|
|
- status = init_complete->status;
|
|
- if (status == RNDIS_STATUS_SUCCESS) {
|
|
- dev->state = RNDIS_DEV_INITIALIZED;
|
|
- ret = 0;
|
|
- } else {
|
|
- dev->state = RNDIS_DEV_UNINITIALIZED;
|
|
- ret = -EINVAL;
|
|
- }
|
|
-
|
|
-cleanup:
|
|
- if (request)
|
|
- put_rndis_request(dev, request);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void rndis_filter_halt_device(struct rndis_device *dev)
|
|
-{
|
|
- struct rndis_request *request;
|
|
- struct rndis_halt_request *halt;
|
|
-
|
|
- /* Attempt to do a rndis device halt */
|
|
- request = get_rndis_request(dev, REMOTE_NDIS_HALT_MSG,
|
|
- RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
|
|
- if (!request)
|
|
- goto cleanup;
|
|
-
|
|
- /* Setup the rndis set */
|
|
- halt = &request->request_msg.msg.halt_req;
|
|
- halt->req_id = atomic_inc_return(&dev->new_req_id);
|
|
-
|
|
- /* Ignore return since this msg is optional. */
|
|
- rndis_filter_send_request(dev, request);
|
|
-
|
|
- dev->state = RNDIS_DEV_UNINITIALIZED;
|
|
-
|
|
-cleanup:
|
|
- if (request)
|
|
- put_rndis_request(dev, request);
|
|
- return;
|
|
-}
|
|
-
|
|
-static int rndis_filter_open_device(struct rndis_device *dev)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- if (dev->state != RNDIS_DEV_INITIALIZED)
|
|
- return 0;
|
|
-
|
|
- ret = rndis_filter_set_packet_filter(dev,
|
|
- NDIS_PACKET_TYPE_BROADCAST |
|
|
- NDIS_PACKET_TYPE_ALL_MULTICAST |
|
|
- NDIS_PACKET_TYPE_DIRECTED);
|
|
- if (ret == 0)
|
|
- dev->state = RNDIS_DEV_DATAINITIALIZED;
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int rndis_filter_close_device(struct rndis_device *dev)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- if (dev->state != RNDIS_DEV_DATAINITIALIZED)
|
|
- return 0;
|
|
-
|
|
- ret = rndis_filter_set_packet_filter(dev, 0);
|
|
- if (ret == 0)
|
|
- dev->state = RNDIS_DEV_INITIALIZED;
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-int rndis_filter_device_add(struct hv_device *dev,
|
|
- void *additional_info)
|
|
-{
|
|
- int ret;
|
|
- struct netvsc_device *net_device;
|
|
- struct rndis_device *rndis_device;
|
|
- struct netvsc_device_info *device_info = additional_info;
|
|
-
|
|
- rndis_device = get_rndis_device();
|
|
- if (!rndis_device)
|
|
- return -ENODEV;
|
|
-
|
|
- /*
|
|
- * Let the inner driver handle this first to create the netvsc channel
|
|
- * NOTE! Once the channel is created, we may get a receive callback
|
|
- * (RndisFilterOnReceive()) before this call is completed
|
|
- */
|
|
- ret = netvsc_device_add(dev, additional_info);
|
|
- if (ret != 0) {
|
|
- kfree(rndis_device);
|
|
- return ret;
|
|
- }
|
|
-
|
|
-
|
|
- /* Initialize the rndis device */
|
|
- net_device = hv_get_drvdata(dev);
|
|
-
|
|
- net_device->extension = rndis_device;
|
|
- rndis_device->net_dev = net_device;
|
|
-
|
|
- /* Send the rndis initialization message */
|
|
- ret = rndis_filter_init_device(rndis_device);
|
|
- if (ret != 0) {
|
|
- /*
|
|
- * TODO: If rndis init failed, we will need to shut down the
|
|
- * channel
|
|
- */
|
|
- }
|
|
-
|
|
- /* Get the mac address */
|
|
- ret = rndis_filter_query_device_mac(rndis_device);
|
|
- if (ret != 0) {
|
|
- /*
|
|
- * TODO: shutdown rndis device and the channel
|
|
- */
|
|
- }
|
|
-
|
|
- memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
|
|
-
|
|
- rndis_filter_query_device_link_status(rndis_device);
|
|
-
|
|
- device_info->link_state = rndis_device->link_state;
|
|
-
|
|
- dev_info(&dev->device, "Device MAC %pM link state %s\n",
|
|
- rndis_device->hw_mac_adr,
|
|
- device_info->link_state ? "down" : "up");
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-void rndis_filter_device_remove(struct hv_device *dev)
|
|
-{
|
|
- struct netvsc_device *net_dev = hv_get_drvdata(dev);
|
|
- struct rndis_device *rndis_dev = net_dev->extension;
|
|
-
|
|
- /* Halt and release the rndis device */
|
|
- rndis_filter_halt_device(rndis_dev);
|
|
-
|
|
- kfree(rndis_dev);
|
|
- net_dev->extension = NULL;
|
|
-
|
|
- netvsc_device_remove(dev);
|
|
-}
|
|
-
|
|
-
|
|
-int rndis_filter_open(struct hv_device *dev)
|
|
-{
|
|
- struct netvsc_device *net_device = hv_get_drvdata(dev);
|
|
-
|
|
- if (!net_device)
|
|
- return -EINVAL;
|
|
-
|
|
- return rndis_filter_open_device(net_device->extension);
|
|
-}
|
|
-
|
|
-int rndis_filter_close(struct hv_device *dev)
|
|
-{
|
|
- struct netvsc_device *netDevice = hv_get_drvdata(dev);
|
|
-
|
|
- if (!netDevice)
|
|
- return -EINVAL;
|
|
-
|
|
- return rndis_filter_close_device(netDevice->extension);
|
|
-}
|
|
-
|
|
-int rndis_filter_send(struct hv_device *dev,
|
|
- struct hv_netvsc_packet *pkt)
|
|
-{
|
|
- int ret;
|
|
- struct rndis_filter_packet *filterPacket;
|
|
- struct rndis_message *rndisMessage;
|
|
- struct rndis_packet *rndisPacket;
|
|
- u32 rndisMessageSize;
|
|
-
|
|
- /* Add the rndis header */
|
|
- filterPacket = (struct rndis_filter_packet *)pkt->extension;
|
|
-
|
|
- memset(filterPacket, 0, sizeof(struct rndis_filter_packet));
|
|
-
|
|
- rndisMessage = &filterPacket->msg;
|
|
- rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet);
|
|
-
|
|
- rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
|
|
- rndisMessage->msg_len = pkt->total_data_buflen +
|
|
- rndisMessageSize;
|
|
-
|
|
- rndisPacket = &rndisMessage->msg.pkt;
|
|
- rndisPacket->data_offset = sizeof(struct rndis_packet);
|
|
- rndisPacket->data_len = pkt->total_data_buflen;
|
|
-
|
|
- pkt->is_data_pkt = true;
|
|
- pkt->page_buf[0].pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT;
|
|
- pkt->page_buf[0].offset =
|
|
- (unsigned long)rndisMessage & (PAGE_SIZE-1);
|
|
- pkt->page_buf[0].len = rndisMessageSize;
|
|
-
|
|
- /* Save the packet send completion and context */
|
|
- filterPacket->completion = pkt->completion.send.send_completion;
|
|
- filterPacket->completion_ctx =
|
|
- pkt->completion.send.send_completion_ctx;
|
|
-
|
|
- /* Use ours */
|
|
- pkt->completion.send.send_completion = rndis_filter_send_completion;
|
|
- pkt->completion.send.send_completion_ctx = filterPacket;
|
|
-
|
|
- ret = netvsc_send(dev, pkt);
|
|
- if (ret != 0) {
|
|
- /*
|
|
- * Reset the completion to originals to allow retries from
|
|
- * above
|
|
- */
|
|
- pkt->completion.send.send_completion =
|
|
- filterPacket->completion;
|
|
- pkt->completion.send.send_completion_ctx =
|
|
- filterPacket->completion_ctx;
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void rndis_filter_send_completion(void *ctx)
|
|
-{
|
|
- struct rndis_filter_packet *filterPacket = ctx;
|
|
-
|
|
- /* Pass it back to the original handler */
|
|
- filterPacket->completion(filterPacket->completion_ctx);
|
|
-}
|
|
-
|
|
-
|
|
-static void rndis_filter_send_request_completion(void *ctx)
|
|
-{
|
|
- /* Noop */
|
|
-}
|
|
--
|
|
1.7.9.5
|
|
|