aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/hv
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/hv')
-rw-r--r--drivers/staging/hv/BlkVsc.c111
-rw-r--r--drivers/staging/hv/Channel.c1015
-rw-r--r--drivers/staging/hv/Channel.h112
-rw-r--r--drivers/staging/hv/ChannelInterface.c152
-rw-r--r--drivers/staging/hv/ChannelInterface.h35
-rw-r--r--drivers/staging/hv/ChannelMgmt.c686
-rw-r--r--drivers/staging/hv/ChannelMgmt.h319
-rw-r--r--drivers/staging/hv/Connection.c341
-rw-r--r--drivers/staging/hv/Hv.c568
-rw-r--r--drivers/staging/hv/Hv.h144
-rw-r--r--drivers/staging/hv/Kconfig32
-rw-r--r--drivers/staging/hv/Makefile11
-rw-r--r--drivers/staging/hv/NetVsc.c1379
-rw-r--r--drivers/staging/hv/NetVsc.h329
-rw-r--r--drivers/staging/hv/NetVscApi.h123
-rw-r--r--drivers/staging/hv/RingBuffer.c606
-rw-r--r--drivers/staging/hv/RingBuffer.h101
-rw-r--r--drivers/staging/hv/RndisFilter.c1000
-rw-r--r--drivers/staging/hv/RndisFilter.h55
-rw-r--r--drivers/staging/hv/StorVsc.c850
-rw-r--r--drivers/staging/hv/StorVscApi.h113
-rw-r--r--drivers/staging/hv/TODO13
-rw-r--r--drivers/staging/hv/VersionInfo.h31
-rw-r--r--drivers/staging/hv/Vmbus.c311
-rw-r--r--drivers/staging/hv/VmbusApi.h175
-rw-r--r--drivers/staging/hv/VmbusChannelInterface.h89
-rw-r--r--drivers/staging/hv/VmbusPacketFormat.h160
-rw-r--r--drivers/staging/hv/VmbusPrivate.h134
-rw-r--r--drivers/staging/hv/blkvsc_drv.c1511
-rw-r--r--drivers/staging/hv/hv_api.h905
-rw-r--r--drivers/staging/hv/logging.h119
-rw-r--r--drivers/staging/hv/netvsc_drv.c618
-rw-r--r--drivers/staging/hv/osd.c156
-rw-r--r--drivers/staging/hv/osd.h69
-rw-r--r--drivers/staging/hv/rndis.h652
-rw-r--r--drivers/staging/hv/storvsc_drv.c1208
-rw-r--r--drivers/staging/hv/vmbus.h77
-rw-r--r--drivers/staging/hv/vmbus_drv.c999
-rw-r--r--drivers/staging/hv/vstorage.h192
39 files changed, 15501 insertions, 0 deletions
diff --git a/drivers/staging/hv/BlkVsc.c b/drivers/staging/hv/BlkVsc.c
new file mode 100644
index 000000000000..51aa861292fc
--- /dev/null
+++ b/drivers/staging/hv/BlkVsc.c
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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:
+ * Hank Janssen <hjanssen@microsoft.com>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include "osd.h"
+#include "StorVsc.c"
+
+static const char *gBlkDriverName = "blkvsc";
+
+/* {32412632-86cb-44a2-9b5c-50d1417354f5} */
+static const struct hv_guid gBlkVscDeviceType = {
+ .data = {
+ 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
+ 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
+ }
+};
+
+static int BlkVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo)
+{
+ struct storvsc_device_info *deviceInfo;
+ int ret = 0;
+
+ DPRINT_ENTER(BLKVSC);
+
+ deviceInfo = (struct storvsc_device_info *)AdditionalInfo;
+
+ ret = StorVscOnDeviceAdd(Device, AdditionalInfo);
+ if (ret != 0) {
+ DPRINT_EXIT(BLKVSC);
+ return ret;
+ }
+
+ /*
+ * We need to use the device instance guid to set the path and target
+ * id. For IDE devices, the device instance id is formatted as
+ * <bus id> * - <device id> - 8899 - 000000000000.
+ */
+ deviceInfo->PathId = Device->deviceInstance.data[3] << 24 |
+ Device->deviceInstance.data[2] << 16 |
+ Device->deviceInstance.data[1] << 8 |
+ Device->deviceInstance.data[0];
+
+ deviceInfo->TargetId = Device->deviceInstance.data[5] << 8 |
+ Device->deviceInstance.data[4];
+
+ DPRINT_EXIT(BLKVSC);
+
+ return ret;
+}
+
+int BlkVscInitialize(struct hv_driver *Driver)
+{
+ struct storvsc_driver_object *storDriver;
+ int ret = 0;
+
+ DPRINT_ENTER(BLKVSC);
+
+ storDriver = (struct storvsc_driver_object *)Driver;
+
+ /* Make sure we are at least 2 pages since 1 page is used for control */
+ ASSERT(storDriver->RingBufferSize >= (PAGE_SIZE << 1));
+
+ Driver->name = gBlkDriverName;
+ memcpy(&Driver->deviceType, &gBlkVscDeviceType, sizeof(struct hv_guid));
+
+ storDriver->RequestExtSize = sizeof(struct storvsc_request_extension);
+
+ /*
+ * Divide the ring buffer data size (which is 1 page less than the ring
+ * buffer size since that page is reserved for the ring buffer indices)
+ * by the max request size (which is
+ * VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER + struct vstor_packet + u64)
+ */
+ storDriver->MaxOutstandingRequestsPerChannel =
+ ((storDriver->RingBufferSize - PAGE_SIZE) /
+ ALIGN_UP(MAX_MULTIPAGE_BUFFER_PACKET +
+ sizeof(struct vstor_packet) + sizeof(u64),
+ sizeof(u64)));
+
+ DPRINT_INFO(BLKVSC, "max io outstd %u",
+ storDriver->MaxOutstandingRequestsPerChannel);
+
+ /* Setup the dispatch table */
+ storDriver->Base.OnDeviceAdd = BlkVscOnDeviceAdd;
+ storDriver->Base.OnDeviceRemove = StorVscOnDeviceRemove;
+ storDriver->Base.OnCleanup = StorVscOnCleanup;
+ storDriver->OnIORequest = StorVscOnIORequest;
+
+ DPRINT_EXIT(BLKVSC);
+
+ return ret;
+}
diff --git a/drivers/staging/hv/Channel.c b/drivers/staging/hv/Channel.c
new file mode 100644
index 000000000000..d649ee169d95
--- /dev/null
+++ b/drivers/staging/hv/Channel.c
@@ -0,0 +1,1015 @@
+/*
+ * 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/mm.h>
+#include "osd.h"
+#include "logging.h"
+#include "VmbusPrivate.h"
+
+/* Internal routines */
+static int VmbusChannelCreateGpadlHeader(
+ void *Kbuffer, /* must be phys and virt contiguous */
+ u32 Size, /* page-size multiple */
+ struct vmbus_channel_msginfo **msgInfo,
+ u32 *MessageCount);
+static void DumpVmbusChannel(struct vmbus_channel *channel);
+static void VmbusChannelSetEvent(struct vmbus_channel *channel);
+
+
+#if 0
+static void DumpMonitorPage(struct hv_monitor_page *MonitorPage)
+{
+ int i = 0;
+ int j = 0;
+
+ DPRINT_DBG(VMBUS, "monitorPage - %p, trigger state - %d",
+ MonitorPage, MonitorPage->TriggerState);
+
+ for (i = 0; i < 4; i++)
+ DPRINT_DBG(VMBUS, "trigger group (%d) - %llx", i,
+ MonitorPage->TriggerGroup[i].AsUINT64);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 32; j++) {
+ DPRINT_DBG(VMBUS, "latency (%d)(%d) - %llx", i, j,
+ MonitorPage->Latency[i][j]);
+ }
+ }
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 32; j++) {
+ DPRINT_DBG(VMBUS, "param-conn id (%d)(%d) - %d", i, j,
+ MonitorPage->Parameter[i][j].ConnectionId.Asu32);
+ DPRINT_DBG(VMBUS, "param-flag (%d)(%d) - %d", i, j,
+ MonitorPage->Parameter[i][j].FlagNumber);
+ }
+ }
+}
+#endif
+
+/**
+ * VmbusChannelSetEvent - Trigger an event notification on the specified channel.
+ */
+static void VmbusChannelSetEvent(struct vmbus_channel *Channel)
+{
+ struct hv_monitor_page *monitorPage;
+
+ DPRINT_ENTER(VMBUS);
+
+ if (Channel->OfferMsg.MonitorAllocated) {
+ /* Each u32 represents 32 channels */
+ set_bit(Channel->OfferMsg.ChildRelId & 31,
+ (unsigned long *) gVmbusConnection.SendInterruptPage +
+ (Channel->OfferMsg.ChildRelId >> 5));
+
+ monitorPage = gVmbusConnection.MonitorPages;
+ monitorPage++; /* Get the child to parent monitor page */
+
+ set_bit(Channel->MonitorBit,
+ (unsigned long *)&monitorPage->TriggerGroup
+ [Channel->MonitorGroup].Pending);
+
+ } else {
+ VmbusSetEvent(Channel->OfferMsg.ChildRelId);
+ }
+
+ DPRINT_EXIT(VMBUS);
+}
+
+#if 0
+static void VmbusChannelClearEvent(struct vmbus_channel *channel)
+{
+ struct hv_monitor_page *monitorPage;
+
+ DPRINT_ENTER(VMBUS);
+
+ if (Channel->OfferMsg.MonitorAllocated) {
+ /* Each u32 represents 32 channels */
+ clear_bit(Channel->OfferMsg.ChildRelId & 31,
+ (unsigned long *)gVmbusConnection.SendInterruptPage +
+ (Channel->OfferMsg.ChildRelId >> 5));
+
+ monitorPage =
+ (struct hv_monitor_page *)gVmbusConnection.MonitorPages;
+ monitorPage++; /* Get the child to parent monitor page */
+
+ clear_bit(Channel->MonitorBit,
+ (unsigned long *)&monitorPage->TriggerGroup
+ [Channel->MonitorGroup].Pending);
+ }
+
+ DPRINT_EXIT(VMBUS);
+}
+
+#endif
+/**
+ * VmbusChannelGetDebugInfo -Retrieve various channel debug info
+ */
+void VmbusChannelGetDebugInfo(struct vmbus_channel *Channel,
+ struct vmbus_channel_debug_info *DebugInfo)
+{
+ struct hv_monitor_page *monitorPage;
+ u8 monitorGroup = (u8)Channel->OfferMsg.MonitorId / 32;
+ u8 monitorOffset = (u8)Channel->OfferMsg.MonitorId % 32;
+ /* u32 monitorBit = 1 << monitorOffset; */
+
+ DebugInfo->RelId = Channel->OfferMsg.ChildRelId;
+ DebugInfo->State = Channel->State;
+ memcpy(&DebugInfo->InterfaceType,
+ &Channel->OfferMsg.Offer.InterfaceType, sizeof(struct hv_guid));
+ memcpy(&DebugInfo->InterfaceInstance,
+ &Channel->OfferMsg.Offer.InterfaceInstance,
+ sizeof(struct hv_guid));
+
+ monitorPage = (struct hv_monitor_page *)gVmbusConnection.MonitorPages;
+
+ DebugInfo->MonitorId = Channel->OfferMsg.MonitorId;
+
+ DebugInfo->ServerMonitorPending =
+ monitorPage->TriggerGroup[monitorGroup].Pending;
+ DebugInfo->ServerMonitorLatency =
+ monitorPage->Latency[monitorGroup][monitorOffset];
+ DebugInfo->ServerMonitorConnectionId =
+ monitorPage->Parameter[monitorGroup]
+ [monitorOffset].ConnectionId.u.Id;
+
+ monitorPage++;
+
+ DebugInfo->ClientMonitorPending =
+ monitorPage->TriggerGroup[monitorGroup].Pending;
+ DebugInfo->ClientMonitorLatency =
+ monitorPage->Latency[monitorGroup][monitorOffset];
+ DebugInfo->ClientMonitorConnectionId =
+ monitorPage->Parameter[monitorGroup]
+ [monitorOffset].ConnectionId.u.Id;
+
+ RingBufferGetDebugInfo(&Channel->Inbound, &DebugInfo->Inbound);
+ RingBufferGetDebugInfo(&Channel->Outbound, &DebugInfo->Outbound);
+}
+
+/**
+ * VmbusChannelOpen - Open the specified channel.
+ */
+int VmbusChannelOpen(struct vmbus_channel *NewChannel, u32 SendRingBufferSize,
+ u32 RecvRingBufferSize, void *UserData, u32 UserDataLen,
+ void (*OnChannelCallback)(void *context), void *Context)
+{
+ struct vmbus_channel_open_channel *openMsg;
+ struct vmbus_channel_msginfo *openInfo;
+ void *in, *out;
+ unsigned long flags;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Aligned to page size */
+ ASSERT(!(SendRingBufferSize & (PAGE_SIZE - 1)));
+ ASSERT(!(RecvRingBufferSize & (PAGE_SIZE - 1)));
+
+ NewChannel->OnChannelCallback = OnChannelCallback;
+ NewChannel->ChannelCallbackContext = Context;
+
+ /* Allocate the ring buffer */
+ out = osd_PageAlloc((SendRingBufferSize + RecvRingBufferSize)
+ >> PAGE_SHIFT);
+ ASSERT(out);
+ ASSERT(((unsigned long)out & (PAGE_SIZE-1)) == 0);
+
+ in = (void *)((unsigned long)out + SendRingBufferSize);
+
+ NewChannel->RingBufferPages = out;
+ NewChannel->RingBufferPageCount = (SendRingBufferSize +
+ RecvRingBufferSize) >> PAGE_SHIFT;
+
+ RingBufferInit(&NewChannel->Outbound, out, SendRingBufferSize);
+
+ RingBufferInit(&NewChannel->Inbound, in, RecvRingBufferSize);
+
+ /* Establish the gpadl for the ring buffer */
+ DPRINT_DBG(VMBUS, "Establishing ring buffer's gpadl for channel %p...",
+ NewChannel);
+
+ NewChannel->RingBufferGpadlHandle = 0;
+
+ ret = VmbusChannelEstablishGpadl(NewChannel,
+ NewChannel->Outbound.RingBuffer,
+ SendRingBufferSize +
+ RecvRingBufferSize,
+ &NewChannel->RingBufferGpadlHandle);
+
+ DPRINT_DBG(VMBUS, "channel %p <relid %d gpadl 0x%x send ring %p "
+ "size %d recv ring %p size %d, downstreamoffset %d>",
+ NewChannel, NewChannel->OfferMsg.ChildRelId,
+ NewChannel->RingBufferGpadlHandle,
+ NewChannel->Outbound.RingBuffer,
+ NewChannel->Outbound.RingSize,
+ NewChannel->Inbound.RingBuffer,
+ NewChannel->Inbound.RingSize,
+ SendRingBufferSize);
+
+ /* Create and init the channel open message */
+ openInfo = kmalloc(sizeof(*openInfo) +
+ sizeof(struct vmbus_channel_open_channel),
+ GFP_KERNEL);
+ ASSERT(openInfo != NULL);
+
+ openInfo->WaitEvent = osd_WaitEventCreate();
+
+ openMsg = (struct vmbus_channel_open_channel *)openInfo->Msg;
+ openMsg->Header.MessageType = ChannelMessageOpenChannel;
+ openMsg->OpenId = NewChannel->OfferMsg.ChildRelId; /* FIXME */
+ openMsg->ChildRelId = NewChannel->OfferMsg.ChildRelId;
+ openMsg->RingBufferGpadlHandle = NewChannel->RingBufferGpadlHandle;
+ ASSERT(openMsg->RingBufferGpadlHandle);
+ openMsg->DownstreamRingBufferPageOffset = SendRingBufferSize >>
+ PAGE_SHIFT;
+ openMsg->ServerContextAreaGpadlHandle = 0; /* TODO */
+
+ ASSERT(UserDataLen <= MAX_USER_DEFINED_BYTES);
+ if (UserDataLen)
+ memcpy(openMsg->UserData, UserData, UserDataLen);
+
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+ list_add_tail(&openInfo->MsgListEntry,
+ &gVmbusConnection.ChannelMsgList);
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ DPRINT_DBG(VMBUS, "Sending channel open msg...");
+
+ ret = VmbusPostMessage(openMsg,
+ sizeof(struct vmbus_channel_open_channel));
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS, "unable to open channel - %d", ret);
+ goto Cleanup;
+ }
+
+ /* FIXME: Need to time-out here */
+ osd_WaitEventWait(openInfo->WaitEvent);
+
+ if (openInfo->Response.OpenResult.Status == 0)
+ DPRINT_INFO(VMBUS, "channel <%p> open success!!", NewChannel);
+ else
+ DPRINT_INFO(VMBUS, "channel <%p> open failed - %d!!",
+ NewChannel, openInfo->Response.OpenResult.Status);
+
+Cleanup:
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+ list_del(&openInfo->MsgListEntry);
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ kfree(openInfo->WaitEvent);
+ kfree(openInfo);
+
+ DPRINT_EXIT(VMBUS);
+
+ return 0;
+}
+
+/**
+ * DumpGpadlBody - Dump the gpadl body message to the console for debugging purposes.
+ */
+static void DumpGpadlBody(struct vmbus_channel_gpadl_body *Gpadl, u32 Len)
+{
+ int i;
+ int pfnCount;
+
+ pfnCount = (Len - sizeof(struct vmbus_channel_gpadl_body)) /
+ sizeof(u64);
+ DPRINT_DBG(VMBUS, "gpadl body - len %d pfn count %d", Len, pfnCount);
+
+ for (i = 0; i < pfnCount; i++)
+ DPRINT_DBG(VMBUS, "gpadl body - %d) pfn %llu",
+ i, Gpadl->Pfn[i]);
+}
+
+/**
+ * DumpGpadlHeader - Dump the gpadl header message to the console for debugging purposes.
+ */
+static void DumpGpadlHeader(struct vmbus_channel_gpadl_header *Gpadl)
+{
+ int i, j;
+ int pageCount;
+
+ DPRINT_DBG(VMBUS,
+ "gpadl header - relid %d, range count %d, range buflen %d",
+ Gpadl->ChildRelId, Gpadl->RangeCount, Gpadl->RangeBufLen);
+ for (i = 0; i < Gpadl->RangeCount; i++) {
+ pageCount = Gpadl->Range[i].ByteCount >> PAGE_SHIFT;
+ pageCount = (pageCount > 26) ? 26 : pageCount;
+
+ DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d "
+ "page count %d", i, Gpadl->Range[i].ByteCount,
+ Gpadl->Range[i].ByteOffset, pageCount);
+
+ for (j = 0; j < pageCount; j++)
+ DPRINT_DBG(VMBUS, "%d) pfn %llu", j,
+ Gpadl->Range[i].PfnArray[j]);
+ }
+}
+
+/**
+ * VmbusChannelCreateGpadlHeader - Creates a gpadl for the specified buffer
+ */
+static int VmbusChannelCreateGpadlHeader(void *Kbuffer, u32 Size,
+ struct vmbus_channel_msginfo **MsgInfo,
+ u32 *MessageCount)
+{
+ int i;
+ int pageCount;
+ unsigned long long pfn;
+ struct vmbus_channel_gpadl_header *gpaHeader;
+ struct vmbus_channel_gpadl_body *gpadlBody;
+ struct vmbus_channel_msginfo *msgHeader;
+ struct vmbus_channel_msginfo *msgBody;
+ u32 msgSize;
+
+ int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
+
+ /* ASSERT((kbuffer & (PAGE_SIZE-1)) == 0); */
+ ASSERT((Size & (PAGE_SIZE-1)) == 0);
+
+ pageCount = Size >> PAGE_SHIFT;
+ pfn = virt_to_phys(Kbuffer) >> PAGE_SHIFT;
+
+ /* do we need a gpadl body msg */
+ pfnSize = MAX_SIZE_CHANNEL_MESSAGE -
+ sizeof(struct vmbus_channel_gpadl_header) -
+ sizeof(struct gpa_range);
+ pfnCount = pfnSize / sizeof(u64);
+
+ if (pageCount > pfnCount) {
+ /* we need a gpadl body */
+ /* fill in the header */
+ msgSize = sizeof(struct vmbus_channel_msginfo) +
+ sizeof(struct vmbus_channel_gpadl_header) +
+ sizeof(struct gpa_range) + pfnCount * sizeof(u64);
+ msgHeader = kzalloc(msgSize, GFP_KERNEL);
+
+ INIT_LIST_HEAD(&msgHeader->SubMsgList);
+ msgHeader->MessageSize = msgSize;
+
+ gpaHeader = (struct vmbus_channel_gpadl_header *)msgHeader->Msg;
+ gpaHeader->RangeCount = 1;
+ gpaHeader->RangeBufLen = sizeof(struct gpa_range) +
+ pageCount * sizeof(u64);
+ gpaHeader->Range[0].ByteOffset = 0;
+ gpaHeader->Range[0].ByteCount = Size;
+ for (i = 0; i < pfnCount; i++)
+ gpaHeader->Range[0].PfnArray[i] = pfn+i;
+ *MsgInfo = msgHeader;
+ *MessageCount = 1;
+
+ pfnSum = pfnCount;
+ pfnLeft = pageCount - pfnCount;
+
+ /* how many pfns can we fit */
+ pfnSize = MAX_SIZE_CHANNEL_MESSAGE -
+ sizeof(struct vmbus_channel_gpadl_body);
+ pfnCount = pfnSize / sizeof(u64);
+
+ /* fill in the body */
+ while (pfnLeft) {
+ if (pfnLeft > pfnCount)
+ pfnCurr = pfnCount;
+ else
+ pfnCurr = pfnLeft;
+
+ msgSize = sizeof(struct vmbus_channel_msginfo) +
+ sizeof(struct vmbus_channel_gpadl_body) +
+ pfnCurr * sizeof(u64);
+ msgBody = kzalloc(msgSize, GFP_KERNEL);
+ ASSERT(msgBody);
+ msgBody->MessageSize = msgSize;
+ (*MessageCount)++;
+ gpadlBody =
+ (struct vmbus_channel_gpadl_body *)msgBody->Msg;
+
+ /*
+ * FIXME:
+ * Gpadl is u32 and we are using a pointer which could
+ * be 64-bit
+ */
+ /* gpadlBody->Gpadl = kbuffer; */
+ for (i = 0; i < pfnCurr; i++)
+ gpadlBody->Pfn[i] = pfn + pfnSum + i;
+
+ /* add to msg header */
+ list_add_tail(&msgBody->MsgListEntry,
+ &msgHeader->SubMsgList);
+ pfnSum += pfnCurr;
+ pfnLeft -= pfnCurr;
+ }
+ } else {
+ /* everything fits in a header */
+ msgSize = sizeof(struct vmbus_channel_msginfo) +
+ sizeof(struct vmbus_channel_gpadl_header) +
+ sizeof(struct gpa_range) + pageCount * sizeof(u64);
+ msgHeader = kzalloc(msgSize, GFP_KERNEL);
+ msgHeader->MessageSize = msgSize;
+
+ gpaHeader = (struct vmbus_channel_gpadl_header *)msgHeader->Msg;
+ gpaHeader->RangeCount = 1;
+ gpaHeader->RangeBufLen = sizeof(struct gpa_range) +
+ pageCount * sizeof(u64);
+ gpaHeader->Range[0].ByteOffset = 0;
+ gpaHeader->Range[0].ByteCount = Size;
+ for (i = 0; i < pageCount; i++)
+ gpaHeader->Range[0].PfnArray[i] = pfn+i;
+
+ *MsgInfo = msgHeader;
+ *MessageCount = 1;
+ }
+
+ return 0;
+}
+
+/**
+ * VmbusChannelEstablishGpadl - Estabish a GPADL for the specified buffer
+ *
+ * @Channel: a channel
+ * @Kbuffer: from kmalloc
+ * @Size: page-size multiple
+ * @GpadlHandle: some funky thing
+ */
+int VmbusChannelEstablishGpadl(struct vmbus_channel *Channel, void *Kbuffer,
+ u32 Size, u32 *GpadlHandle)
+{
+ struct vmbus_channel_gpadl_header *gpadlMsg;
+ struct vmbus_channel_gpadl_body *gpadlBody;
+ /* struct vmbus_channel_gpadl_created *gpadlCreated; */
+ struct vmbus_channel_msginfo *msgInfo;
+ struct vmbus_channel_msginfo *subMsgInfo;
+ u32 msgCount;
+ struct list_head *curr;
+ u32 nextGpadlHandle;
+ unsigned long flags;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+
+ nextGpadlHandle = atomic_read(&gVmbusConnection.NextGpadlHandle);
+ atomic_inc(&gVmbusConnection.NextGpadlHandle);
+
+ VmbusChannelCreateGpadlHeader(Kbuffer, Size, &msgInfo, &msgCount);
+ ASSERT(msgInfo != NULL);
+ ASSERT(msgCount > 0);
+
+ msgInfo->WaitEvent = osd_WaitEventCreate();
+ gpadlMsg = (struct vmbus_channel_gpadl_header *)msgInfo->Msg;
+ gpadlMsg->Header.MessageType = ChannelMessageGpadlHeader;
+ gpadlMsg->ChildRelId = Channel->OfferMsg.ChildRelId;
+ gpadlMsg->Gpadl = nextGpadlHandle;
+
+ DumpGpadlHeader(gpadlMsg);
+
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+ list_add_tail(&msgInfo->MsgListEntry,
+ &gVmbusConnection.ChannelMsgList);
+
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+ DPRINT_DBG(VMBUS, "buffer %p, size %d msg cnt %d",
+ Kbuffer, Size, msgCount);
+
+ DPRINT_DBG(VMBUS, "Sending GPADL Header - len %zd",
+ msgInfo->MessageSize - sizeof(*msgInfo));
+
+ ret = VmbusPostMessage(gpadlMsg, msgInfo->MessageSize -
+ sizeof(*msgInfo));
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS, "Unable to open channel - %d", ret);
+ goto Cleanup;
+ }
+
+ if (msgCount > 1) {
+ list_for_each(curr, &msgInfo->SubMsgList) {
+
+ /* FIXME: should this use list_entry() instead ? */
+ subMsgInfo = (struct vmbus_channel_msginfo *)curr;
+ gpadlBody =
+ (struct vmbus_channel_gpadl_body *)subMsgInfo->Msg;
+
+ gpadlBody->Header.MessageType = ChannelMessageGpadlBody;
+ gpadlBody->Gpadl = nextGpadlHandle;
+
+ DPRINT_DBG(VMBUS, "Sending GPADL Body - len %zd",
+ subMsgInfo->MessageSize -
+ sizeof(*subMsgInfo));
+
+ DumpGpadlBody(gpadlBody, subMsgInfo->MessageSize -
+ sizeof(*subMsgInfo));
+ ret = VmbusPostMessage(gpadlBody,
+ subMsgInfo->MessageSize -
+ sizeof(*subMsgInfo));
+ ASSERT(ret == 0);
+ }
+ }
+ osd_WaitEventWait(msgInfo->WaitEvent);
+
+ /* At this point, we received the gpadl created msg */
+ DPRINT_DBG(VMBUS, "Received GPADL created "
+ "(relid %d, status %d handle %x)",
+ Channel->OfferMsg.ChildRelId,
+ msgInfo->Response.GpadlCreated.CreationStatus,
+ gpadlMsg->Gpadl);
+
+ *GpadlHandle = gpadlMsg->Gpadl;
+
+Cleanup:
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+ list_del(&msgInfo->MsgListEntry);
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ kfree(msgInfo->WaitEvent);
+ kfree(msgInfo);
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusChannelTeardownGpadl -Teardown the specified GPADL handle
+ */
+int VmbusChannelTeardownGpadl(struct vmbus_channel *Channel, u32 GpadlHandle)
+{
+ struct vmbus_channel_gpadl_teardown *msg;
+ struct vmbus_channel_msginfo *info;
+ unsigned long flags;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+
+ ASSERT(GpadlHandle != 0);
+
+ info = kmalloc(sizeof(*info) +
+ sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL);
+ ASSERT(info != NULL);
+
+ info->WaitEvent = osd_WaitEventCreate();
+
+ msg = (struct vmbus_channel_gpadl_teardown *)info->Msg;
+
+ msg->Header.MessageType = ChannelMessageGpadlTeardown;
+ msg->ChildRelId = Channel->OfferMsg.ChildRelId;
+ msg->Gpadl = GpadlHandle;
+
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+ list_add_tail(&info->MsgListEntry,
+ &gVmbusConnection.ChannelMsgList);
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ ret = VmbusPostMessage(msg,
+ sizeof(struct vmbus_channel_gpadl_teardown));
+ if (ret != 0) {
+ /* TODO: */
+ /* something... */
+ }
+
+ osd_WaitEventWait(info->WaitEvent);
+
+ /* Received a torndown response */
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+ list_del(&info->MsgListEntry);
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ kfree(info->WaitEvent);
+ kfree(info);
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusChannelClose - Close the specified channel
+ */
+void VmbusChannelClose(struct vmbus_channel *Channel)
+{
+ struct vmbus_channel_close_channel *msg;
+ struct vmbus_channel_msginfo *info;
+ unsigned long flags;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Stop callback and cancel the timer asap */
+ Channel->OnChannelCallback = NULL;
+ del_timer(&Channel->poll_timer);
+
+ /* Send a closing message */
+ info = kmalloc(sizeof(*info) +
+ sizeof(struct vmbus_channel_close_channel), GFP_KERNEL);
+ ASSERT(info != NULL);
+
+ /* info->waitEvent = osd_WaitEventCreate(); */
+
+ msg = (struct vmbus_channel_close_channel *)info->Msg;
+ msg->Header.MessageType = ChannelMessageCloseChannel;
+ msg->ChildRelId = Channel->OfferMsg.ChildRelId;
+
+ ret = VmbusPostMessage(msg, sizeof(struct vmbus_channel_close_channel));
+ if (ret != 0) {
+ /* TODO: */
+ /* something... */
+ }
+
+ /* Tear down the gpadl for the channel's ring buffer */
+ if (Channel->RingBufferGpadlHandle)
+ VmbusChannelTeardownGpadl(Channel,
+ Channel->RingBufferGpadlHandle);
+
+ /* TODO: Send a msg to release the childRelId */
+
+ /* Cleanup the ring buffers for this channel */
+ RingBufferCleanup(&Channel->Outbound);
+ RingBufferCleanup(&Channel->Inbound);
+
+ osd_PageFree(Channel->RingBufferPages, Channel->RingBufferPageCount);
+
+ kfree(info);
+
+ /*
+ * If we are closing the channel during an error path in
+ * opening the channel, don't free the channel since the
+ * caller will free the channel
+ */
+
+ if (Channel->State == CHANNEL_OPEN_STATE) {
+ spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
+ list_del(&Channel->ListEntry);
+ spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
+
+ FreeVmbusChannel(Channel);
+ }
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelSendPacket - Send the specified buffer on the given channel
+ */
+int VmbusChannelSendPacket(struct vmbus_channel *Channel, const void *Buffer,
+ u32 BufferLen, u64 RequestId,
+ enum vmbus_packet_type Type, u32 Flags)
+{
+ struct vmpacket_descriptor desc;
+ u32 packetLen = sizeof(struct vmpacket_descriptor) + BufferLen;
+ u32 packetLenAligned = ALIGN_UP(packetLen, sizeof(u64));
+ struct scatterlist bufferList[3];
+ u64 alignedData = 0;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+ DPRINT_DBG(VMBUS, "channel %p buffer %p len %d",
+ Channel, Buffer, BufferLen);
+
+ DumpVmbusChannel(Channel);
+
+ ASSERT((packetLenAligned - packetLen) < sizeof(u64));
+
+ /* Setup the descriptor */
+ desc.Type = Type; /* VmbusPacketTypeDataInBand; */
+ desc.Flags = Flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */
+ /* in 8-bytes granularity */
+ desc.DataOffset8 = sizeof(struct vmpacket_descriptor) >> 3;
+ desc.Length8 = (u16)(packetLenAligned >> 3);
+ desc.TransactionId = RequestId;
+
+ sg_init_table(bufferList, 3);
+ sg_set_buf(&bufferList[0], &desc, sizeof(struct vmpacket_descriptor));
+ sg_set_buf(&bufferList[1], Buffer, BufferLen);
+ sg_set_buf(&bufferList[2], &alignedData, packetLenAligned - packetLen);
+
+ ret = RingBufferWrite(&Channel->Outbound, bufferList, 3);
+
+ /* TODO: We should determine if this is optional */
+ if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound))
+ VmbusChannelSetEvent(Channel);
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusChannelSendPacketPageBuffer - Send a range of single-page buffer packets using a GPADL Direct packet type.
+ */
+int VmbusChannelSendPacketPageBuffer(struct vmbus_channel *Channel,
+ struct hv_page_buffer PageBuffers[],
+ u32 PageCount, void *Buffer, u32 BufferLen,
+ u64 RequestId)
+{
+ int ret;
+ int i;
+ struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER desc;
+ u32 descSize;
+ u32 packetLen;
+ u32 packetLenAligned;
+ struct scatterlist bufferList[3];
+ u64 alignedData = 0;
+
+ DPRINT_ENTER(VMBUS);
+
+ ASSERT(PageCount <= MAX_PAGE_BUFFER_COUNT);
+
+ DumpVmbusChannel(Channel);
+
+ /*
+ * Adjust the size down since VMBUS_CHANNEL_PACKET_PAGE_BUFFER is the
+ * largest size we support
+ */
+ descSize = sizeof(struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER) -
+ ((MAX_PAGE_BUFFER_COUNT - PageCount) *
+ sizeof(struct hv_page_buffer));
+ packetLen = descSize + BufferLen;
+ packetLenAligned = ALIGN_UP(packetLen, sizeof(u64));
+
+ ASSERT((packetLenAligned - packetLen) < sizeof(u64));
+
+ /* Setup the descriptor */
+ desc.Type = VmbusPacketTypeDataUsingGpaDirect;
+ desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+ desc.DataOffset8 = descSize >> 3; /* in 8-bytes grandularity */
+ desc.Length8 = (u16)(packetLenAligned >> 3);
+ desc.TransactionId = RequestId;
+ desc.RangeCount = PageCount;
+
+ for (i = 0; i < PageCount; i++) {
+ desc.Range[i].Length = PageBuffers[i].Length;
+ desc.Range[i].Offset = PageBuffers[i].Offset;
+ desc.Range[i].Pfn = PageBuffers[i].Pfn;
+ }
+
+ sg_init_table(bufferList, 3);
+ sg_set_buf(&bufferList[0], &desc, descSize);
+ sg_set_buf(&bufferList[1], Buffer, BufferLen);
+ sg_set_buf(&bufferList[2], &alignedData, packetLenAligned - packetLen);
+
+ ret = RingBufferWrite(&Channel->Outbound, bufferList, 3);
+
+ /* TODO: We should determine if this is optional */
+ if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound))
+ VmbusChannelSetEvent(Channel);
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusChannelSendPacketMultiPageBuffer - Send a multi-page buffer packet using a GPADL Direct packet type.
+ */
+int VmbusChannelSendPacketMultiPageBuffer(struct vmbus_channel *Channel,
+ struct hv_multipage_buffer *MultiPageBuffer,
+ void *Buffer, u32 BufferLen, u64 RequestId)
+{
+ int ret;
+ struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER desc;
+ u32 descSize;
+ u32 packetLen;
+ u32 packetLenAligned;
+ struct scatterlist bufferList[3];
+ u64 alignedData = 0;
+ u32 PfnCount = NUM_PAGES_SPANNED(MultiPageBuffer->Offset,
+ MultiPageBuffer->Length);
+
+ DPRINT_ENTER(VMBUS);
+
+ DumpVmbusChannel(Channel);
+
+ DPRINT_DBG(VMBUS, "data buffer - offset %u len %u pfn count %u",
+ MultiPageBuffer->Offset, MultiPageBuffer->Length, PfnCount);
+
+ ASSERT(PfnCount > 0);
+ ASSERT(PfnCount <= MAX_MULTIPAGE_BUFFER_COUNT);
+
+ /*
+ * Adjust the size down since VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER is
+ * the largest size we support
+ */
+ descSize = sizeof(struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER) -
+ ((MAX_MULTIPAGE_BUFFER_COUNT - PfnCount) *
+ sizeof(u64));
+ packetLen = descSize + BufferLen;
+ packetLenAligned = ALIGN_UP(packetLen, sizeof(u64));
+
+ ASSERT((packetLenAligned - packetLen) < sizeof(u64));
+
+ /* Setup the descriptor */
+ desc.Type = VmbusPacketTypeDataUsingGpaDirect;
+ desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+ desc.DataOffset8 = descSize >> 3; /* in 8-bytes grandularity */
+ desc.Length8 = (u16)(packetLenAligned >> 3);
+ desc.TransactionId = RequestId;
+ desc.RangeCount = 1;
+
+ desc.Range.Length = MultiPageBuffer->Length;
+ desc.Range.Offset = MultiPageBuffer->Offset;
+
+ memcpy(desc.Range.PfnArray, MultiPageBuffer->PfnArray,
+ PfnCount * sizeof(u64));
+
+ sg_init_table(bufferList, 3);
+ sg_set_buf(&bufferList[0], &desc, descSize);
+ sg_set_buf(&bufferList[1], Buffer, BufferLen);
+ sg_set_buf(&bufferList[2], &alignedData, packetLenAligned - packetLen);
+
+ ret = RingBufferWrite(&Channel->Outbound, bufferList, 3);
+
+ /* TODO: We should determine if this is optional */
+ if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound))
+ VmbusChannelSetEvent(Channel);
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusChannelRecvPacket - Retrieve the user packet on the specified channel
+ */
+/* TODO: Do we ever receive a gpa direct packet other than the ones we send ? */
+int VmbusChannelRecvPacket(struct vmbus_channel *Channel, void *Buffer,
+ u32 BufferLen, u32 *BufferActualLen, u64 *RequestId)
+{
+ struct vmpacket_descriptor desc;
+ u32 packetLen;
+ u32 userLen;
+ int ret;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ *BufferActualLen = 0;
+ *RequestId = 0;
+
+ spin_lock_irqsave(&Channel->inbound_lock, flags);
+
+ ret = RingBufferPeek(&Channel->Inbound, &desc,
+ sizeof(struct vmpacket_descriptor));
+ if (ret != 0) {
+ spin_unlock_irqrestore(&Channel->inbound_lock, flags);
+
+ /* DPRINT_DBG(VMBUS, "nothing to read!!"); */
+ DPRINT_EXIT(VMBUS);
+ return 0;
+ }
+
+ /* VmbusChannelClearEvent(Channel); */
+
+ packetLen = desc.Length8 << 3;
+ userLen = packetLen - (desc.DataOffset8 << 3);
+ /* ASSERT(userLen > 0); */
+
+ DPRINT_DBG(VMBUS, "packet received on channel %p relid %d <type %d "
+ "flag %d tid %llx pktlen %d datalen %d> ",
+ Channel, Channel->OfferMsg.ChildRelId, desc.Type,
+ desc.Flags, desc.TransactionId, packetLen, userLen);
+
+ *BufferActualLen = userLen;
+
+ if (userLen > BufferLen) {
+ spin_unlock_irqrestore(&Channel->inbound_lock, flags);
+
+ DPRINT_ERR(VMBUS, "buffer too small - got %d needs %d",
+ BufferLen, userLen);
+ DPRINT_EXIT(VMBUS);
+
+ return -1;
+ }
+
+ *RequestId = desc.TransactionId;
+
+ /* Copy over the packet to the user buffer */
+ ret = RingBufferRead(&Channel->Inbound, Buffer, userLen,
+ (desc.DataOffset8 << 3));
+
+ spin_unlock_irqrestore(&Channel->inbound_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+
+ return 0;
+}
+
+/**
+ * VmbusChannelRecvPacketRaw - Retrieve the raw packet on the specified channel
+ */
+int VmbusChannelRecvPacketRaw(struct vmbus_channel *Channel, void *Buffer,
+ u32 BufferLen, u32 *BufferActualLen,
+ u64 *RequestId)
+{
+ struct vmpacket_descriptor desc;
+ u32 packetLen;
+ u32 userLen;
+ int ret;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ *BufferActualLen = 0;
+ *RequestId = 0;
+
+ spin_lock_irqsave(&Channel->inbound_lock, flags);
+
+ ret = RingBufferPeek(&Channel->Inbound, &desc,
+ sizeof(struct vmpacket_descriptor));
+ if (ret != 0) {
+ spin_unlock_irqrestore(&Channel->inbound_lock, flags);
+
+ /* DPRINT_DBG(VMBUS, "nothing to read!!"); */
+ DPRINT_EXIT(VMBUS);
+ return 0;
+ }
+
+ /* VmbusChannelClearEvent(Channel); */
+
+ packetLen = desc.Length8 << 3;
+ userLen = packetLen - (desc.DataOffset8 << 3);
+
+ DPRINT_DBG(VMBUS, "packet received on channel %p relid %d <type %d "
+ "flag %d tid %llx pktlen %d datalen %d> ",
+ Channel, Channel->OfferMsg.ChildRelId, desc.Type,
+ desc.Flags, desc.TransactionId, packetLen, userLen);
+
+ *BufferActualLen = packetLen;
+
+ if (packetLen > BufferLen) {
+ spin_unlock_irqrestore(&Channel->inbound_lock, flags);
+
+ DPRINT_ERR(VMBUS, "buffer too small - needed %d bytes but "
+ "got space for only %d bytes", packetLen, BufferLen);
+ DPRINT_EXIT(VMBUS);
+ return -2;
+ }
+
+ *RequestId = desc.TransactionId;
+
+ /* Copy over the entire packet to the user buffer */
+ ret = RingBufferRead(&Channel->Inbound, Buffer, packetLen, 0);
+
+ spin_unlock_irqrestore(&Channel->inbound_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+
+ return 0;
+}
+
+/**
+ * VmbusChannelOnChannelEvent - Channel event callback
+ */
+void VmbusChannelOnChannelEvent(struct vmbus_channel *Channel)
+{
+ DumpVmbusChannel(Channel);
+ ASSERT(Channel->OnChannelCallback);
+#ifdef ENABLE_POLLING
+ del_timer(&Channel->poll_timer);
+ Channel->OnChannelCallback(Channel->ChannelCallbackContext);
+ channel->poll_timer.expires(jiffies + usecs_to_jiffies(100);
+ add_timer(&channel->poll_timer);
+#else
+ Channel->OnChannelCallback(Channel->ChannelCallbackContext);
+#endif
+}
+
+/**
+ * VmbusChannelOnTimer - Timer event callback
+ */
+void VmbusChannelOnTimer(unsigned long data)
+{
+ struct vmbus_channel *channel = (struct vmbus_channel *)data;
+
+ if (channel->OnChannelCallback) {
+ channel->OnChannelCallback(channel->ChannelCallbackContext);
+#ifdef ENABLE_POLLING
+ channel->poll_timer.expires(jiffies + usecs_to_jiffies(100);
+ add_timer(&channel->poll_timer);
+#endif
+ }
+}
+
+/**
+ * DumpVmbusChannel - Dump vmbus channel info to the console
+ */
+static void DumpVmbusChannel(struct vmbus_channel *Channel)
+{
+ DPRINT_DBG(VMBUS, "Channel (%d)", Channel->OfferMsg.ChildRelId);
+ DumpRingInfo(&Channel->Outbound, "Outbound ");
+ DumpRingInfo(&Channel->Inbound, "Inbound ");
+}
diff --git a/drivers/staging/hv/Channel.h b/drivers/staging/hv/Channel.h
new file mode 100644
index 000000000000..6b283edcae68
--- /dev/null
+++ b/drivers/staging/hv/Channel.h
@@ -0,0 +1,112 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _CHANNEL_H_
+#define _CHANNEL_H_
+
+#include "ChannelMgmt.h"
+
+/* The format must be the same as struct vmdata_gpa_direct */
+struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER {
+ u16 Type;
+ u16 DataOffset8;
+ u16 Length8;
+ u16 Flags;
+ u64 TransactionId;
+ u32 Reserved;
+ u32 RangeCount;
+ struct hv_page_buffer Range[MAX_PAGE_BUFFER_COUNT];
+} __attribute__((packed));
+
+/* The format must be the same as struct vmdata_gpa_direct */
+struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER {
+ u16 Type;
+ u16 DataOffset8;
+ u16 Length8;
+ u16 Flags;
+ u64 TransactionId;
+ u32 Reserved;
+ u32 RangeCount; /* Always 1 in this case */
+ struct hv_multipage_buffer Range;
+} __attribute__((packed));
+
+
+extern int VmbusChannelOpen(struct vmbus_channel *channel,
+ u32 SendRingBufferSize,
+ u32 RecvRingBufferSize,
+ void *UserData,
+ u32 UserDataLen,
+ void(*OnChannelCallback)(void *context),
+ void *Context);
+
+extern void VmbusChannelClose(struct vmbus_channel *channel);
+
+extern int VmbusChannelSendPacket(struct vmbus_channel *channel,
+ const void *Buffer,
+ u32 BufferLen,
+ u64 RequestId,
+ enum vmbus_packet_type Type,
+ u32 Flags);
+
+extern int VmbusChannelSendPacketPageBuffer(struct vmbus_channel *channel,
+ struct hv_page_buffer PageBuffers[],
+ u32 PageCount,
+ void *Buffer,
+ u32 BufferLen,
+ u64 RequestId);
+
+extern int VmbusChannelSendPacketMultiPageBuffer(struct vmbus_channel *channel,
+ struct hv_multipage_buffer *mpb,
+ void *Buffer,
+ u32 BufferLen,
+ u64 RequestId);
+
+extern int VmbusChannelEstablishGpadl(struct vmbus_channel *channel,
+ void *Kbuffer,
+ u32 Size,
+ u32 *GpadlHandle);
+
+extern int VmbusChannelTeardownGpadl(struct vmbus_channel *channel,
+ u32 GpadlHandle);
+
+extern int VmbusChannelRecvPacket(struct vmbus_channel *channel,
+ void *Buffer,
+ u32 BufferLen,
+ u32 *BufferActualLen,
+ u64 *RequestId);
+
+extern int VmbusChannelRecvPacketRaw(struct vmbus_channel *channel,
+ void *Buffer,
+ u32 BufferLen,
+ u32 *BufferActualLen,
+ u64 *RequestId);
+
+extern void VmbusChannelOnChannelEvent(struct vmbus_channel *channel);
+
+extern void VmbusChannelGetDebugInfo(struct vmbus_channel *channel,
+ struct vmbus_channel_debug_info *debug);
+
+extern void VmbusChannelOnTimer(unsigned long data);
+
+#endif /* _CHANNEL_H_ */
diff --git a/drivers/staging/hv/ChannelInterface.c b/drivers/staging/hv/ChannelInterface.c
new file mode 100644
index 000000000000..019b064f7cb3
--- /dev/null
+++ b/drivers/staging/hv/ChannelInterface.c
@@ -0,0 +1,152 @@
+/*
+ *
+ * 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/mm.h>
+#include "osd.h"
+#include "VmbusPrivate.h"
+
+static int IVmbusChannelOpen(struct hv_device *device, u32 SendBufferSize,
+ u32 RecvRingBufferSize, void *UserData,
+ u32 UserDataLen,
+ void (*ChannelCallback)(void *context),
+ void *Context)
+{
+ return VmbusChannelOpen(device->context, SendBufferSize,
+ RecvRingBufferSize, UserData, UserDataLen,
+ ChannelCallback, Context);
+}
+
+static void IVmbusChannelClose(struct hv_device *device)
+{
+ VmbusChannelClose(device->context);
+}
+
+static int IVmbusChannelSendPacket(struct hv_device *device, const void *Buffer,
+ u32 BufferLen, u64 RequestId, u32 Type,
+ u32 Flags)
+{
+ return VmbusChannelSendPacket(device->context, Buffer, BufferLen,
+ RequestId, Type, Flags);
+}
+
+static int IVmbusChannelSendPacketPageBuffer(struct hv_device *device,
+ struct hv_page_buffer PageBuffers[],
+ u32 PageCount, void *Buffer,
+ u32 BufferLen, u64 RequestId)
+{
+ return VmbusChannelSendPacketPageBuffer(device->context, PageBuffers,
+ PageCount, Buffer, BufferLen,
+ RequestId);
+}
+
+static int IVmbusChannelSendPacketMultiPageBuffer(struct hv_device *device,
+ struct hv_multipage_buffer *MultiPageBuffer,
+ void *Buffer, u32 BufferLen, u64 RequestId)
+{
+ return VmbusChannelSendPacketMultiPageBuffer(device->context,
+ MultiPageBuffer, Buffer,
+ BufferLen, RequestId);
+}
+
+static int IVmbusChannelRecvPacket(struct hv_device *device, void *Buffer,
+ u32 BufferLen, u32 *BufferActualLen,
+ u64 *RequestId)
+{
+ return VmbusChannelRecvPacket(device->context, Buffer, BufferLen,
+ BufferActualLen, RequestId);
+}
+
+static int IVmbusChannelRecvPacketRaw(struct hv_device *device, void *Buffer,
+ u32 BufferLen, u32 *BufferActualLen,
+ u64 *RequestId)
+{
+ return VmbusChannelRecvPacketRaw(device->context, Buffer, BufferLen,
+ BufferActualLen, RequestId);
+}
+
+static int IVmbusChannelEstablishGpadl(struct hv_device *device, void *Buffer,
+ u32 BufferLen, u32 *GpadlHandle)
+{
+ return VmbusChannelEstablishGpadl(device->context, Buffer, BufferLen,
+ GpadlHandle);
+}
+
+static int IVmbusChannelTeardownGpadl(struct hv_device *device, u32 GpadlHandle)
+{
+ return VmbusChannelTeardownGpadl(device->context, GpadlHandle);
+
+}
+
+void GetChannelInterface(struct vmbus_channel_interface *iface)
+{
+ iface->Open = IVmbusChannelOpen;
+ iface->Close = IVmbusChannelClose;
+ iface->SendPacket = IVmbusChannelSendPacket;
+ iface->SendPacketPageBuffer = IVmbusChannelSendPacketPageBuffer;
+ iface->SendPacketMultiPageBuffer =
+ IVmbusChannelSendPacketMultiPageBuffer;
+ iface->RecvPacket = IVmbusChannelRecvPacket;
+ iface->RecvPacketRaw = IVmbusChannelRecvPacketRaw;
+ iface->EstablishGpadl = IVmbusChannelEstablishGpadl;
+ iface->TeardownGpadl = IVmbusChannelTeardownGpadl;
+ iface->GetInfo = GetChannelInfo;
+}
+
+void GetChannelInfo(struct hv_device *device, struct hv_device_info *info)
+{
+ struct vmbus_channel_debug_info debugInfo;
+
+ if (!device->context)
+ return;
+
+ VmbusChannelGetDebugInfo(device->context, &debugInfo);
+
+ info->ChannelId = debugInfo.RelId;
+ info->ChannelState = debugInfo.State;
+ memcpy(&info->ChannelType, &debugInfo.InterfaceType,
+ sizeof(struct hv_guid));
+ memcpy(&info->ChannelInstance, &debugInfo.InterfaceInstance,
+ sizeof(struct hv_guid));
+
+ info->MonitorId = debugInfo.MonitorId;
+
+ info->ServerMonitorPending = debugInfo.ServerMonitorPending;
+ info->ServerMonitorLatency = debugInfo.ServerMonitorLatency;
+ info->ServerMonitorConnectionId = debugInfo.ServerMonitorConnectionId;
+
+ info->ClientMonitorPending = debugInfo.ClientMonitorPending;
+ info->ClientMonitorLatency = debugInfo.ClientMonitorLatency;
+ info->ClientMonitorConnectionId = debugInfo.ClientMonitorConnectionId;
+
+ info->Inbound.InterruptMask = debugInfo.Inbound.CurrentInterruptMask;
+ info->Inbound.ReadIndex = debugInfo.Inbound.CurrentReadIndex;
+ info->Inbound.WriteIndex = debugInfo.Inbound.CurrentWriteIndex;
+ info->Inbound.BytesAvailToRead = debugInfo.Inbound.BytesAvailToRead;
+ info->Inbound.BytesAvailToWrite = debugInfo.Inbound.BytesAvailToWrite;
+
+ info->Outbound.InterruptMask = debugInfo.Outbound.CurrentInterruptMask;
+ info->Outbound.ReadIndex = debugInfo.Outbound.CurrentReadIndex;
+ info->Outbound.WriteIndex = debugInfo.Outbound.CurrentWriteIndex;
+ info->Outbound.BytesAvailToRead = debugInfo.Outbound.BytesAvailToRead;
+ info->Outbound.BytesAvailToWrite = debugInfo.Outbound.BytesAvailToWrite;
+}
diff --git a/drivers/staging/hv/ChannelInterface.h b/drivers/staging/hv/ChannelInterface.h
new file mode 100644
index 000000000000..27b7a253b711
--- /dev/null
+++ b/drivers/staging/hv/ChannelInterface.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _CHANNEL_INTERFACE_H_
+#define _CHANNEL_INTERFACE_H_
+
+#include "VmbusApi.h"
+
+void GetChannelInterface(struct vmbus_channel_interface *ChannelInterface);
+
+void GetChannelInfo(struct hv_device *Device,
+ struct hv_device_info *DeviceInfo);
+
+#endif /* _CHANNEL_INTERFACE_H_ */
diff --git a/drivers/staging/hv/ChannelMgmt.c b/drivers/staging/hv/ChannelMgmt.c
new file mode 100644
index 000000000000..3db62caedcff
--- /dev/null
+++ b/drivers/staging/hv/ChannelMgmt.c
@@ -0,0 +1,686 @@
+/*
+ * 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/mm.h>
+#include <linux/list.h>
+#include "osd.h"
+#include "logging.h"
+#include "VmbusPrivate.h"
+
+struct vmbus_channel_message_table_entry {
+ enum vmbus_channel_message_type messageType;
+ void (*messageHandler)(struct vmbus_channel_message_header *msg);
+};
+
+#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 4
+static const struct hv_guid
+ gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = {
+ /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
+ /* Storage - SCSI */
+ {
+ .data = {
+ 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
+ 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
+ }
+ },
+
+ /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
+ /* Network */
+ {
+ .data = {
+ 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
+ 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
+ }
+ },
+
+ /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
+ /* Input */
+ {
+ .data = {
+ 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
+ 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A
+ }
+ },
+
+ /* {32412632-86cb-44a2-9b5c-50d1417354f5} */
+ /* IDE */
+ {
+ .data = {
+ 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
+ 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
+ }
+ },
+};
+
+/**
+ * AllocVmbusChannel - Allocate and initialize a vmbus channel object
+ */
+struct vmbus_channel *AllocVmbusChannel(void)
+{
+ struct vmbus_channel *channel;
+
+ channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
+ if (!channel)
+ return NULL;
+
+ spin_lock_init(&channel->inbound_lock);
+
+ init_timer(&channel->poll_timer);
+ channel->poll_timer.data = (unsigned long)channel;
+ channel->poll_timer.function = VmbusChannelOnTimer;
+
+ channel->ControlWQ = create_workqueue("hv_vmbus_ctl");
+ if (!channel->ControlWQ) {
+ kfree(channel);
+ return NULL;
+ }
+
+ return channel;
+}
+
+/**
+ * ReleaseVmbusChannel - Release the vmbus channel object itself
+ */
+static inline void ReleaseVmbusChannel(void *context)
+{
+ struct vmbus_channel *channel = context;
+
+ DPRINT_ENTER(VMBUS);
+
+ DPRINT_DBG(VMBUS, "releasing channel (%p)", channel);
+ destroy_workqueue(channel->ControlWQ);
+ DPRINT_DBG(VMBUS, "channel released (%p)", channel);
+
+ kfree(channel);
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * FreeVmbusChannel - Release the resources used by the vmbus channel object
+ */
+void FreeVmbusChannel(struct vmbus_channel *Channel)
+{
+ del_timer(&Channel->poll_timer);
+
+ /*
+ * We have to release the channel's workqueue/thread in the vmbus's
+ * workqueue/thread context
+ * ie we can't destroy ourselves.
+ */
+ osd_schedule_callback(gVmbusConnection.WorkQueue, ReleaseVmbusChannel,
+ Channel);
+}
+
+/**
+ * VmbusChannelProcessOffer - Process the offer by creating a channel/device associated with this offer
+ */
+static void VmbusChannelProcessOffer(void *context)
+{
+ struct vmbus_channel *newChannel = context;
+ struct vmbus_channel *channel;
+ bool fNew = true;
+ int ret;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Make sure this is a new offer */
+ spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
+
+ list_for_each_entry(channel, &gVmbusConnection.ChannelList, ListEntry) {
+ if (!memcmp(&channel->OfferMsg.Offer.InterfaceType,
+ &newChannel->OfferMsg.Offer.InterfaceType,
+ sizeof(struct hv_guid)) &&
+ !memcmp(&channel->OfferMsg.Offer.InterfaceInstance,
+ &newChannel->OfferMsg.Offer.InterfaceInstance,
+ sizeof(struct hv_guid))) {
+ fNew = false;
+ break;
+ }
+ }
+
+ if (fNew)
+ list_add_tail(&newChannel->ListEntry,
+ &gVmbusConnection.ChannelList);
+
+ spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
+
+ if (!fNew) {
+ DPRINT_DBG(VMBUS, "Ignoring duplicate offer for relid (%d)",
+ newChannel->OfferMsg.ChildRelId);
+ FreeVmbusChannel(newChannel);
+ DPRINT_EXIT(VMBUS);
+ return;
+ }
+
+ /*
+ * Start the process of binding this offer to the driver
+ * We need to set the DeviceObject field before calling
+ * VmbusChildDeviceAdd()
+ */
+ newChannel->DeviceObject = VmbusChildDeviceCreate(
+ &newChannel->OfferMsg.Offer.InterfaceType,
+ &newChannel->OfferMsg.Offer.InterfaceInstance,
+ newChannel);
+
+ DPRINT_DBG(VMBUS, "child device object allocated - %p",
+ newChannel->DeviceObject);
+
+ /*
+ * Add the new device to the bus. This will kick off device-driver
+ * binding which eventually invokes the device driver's AddDevice()
+ * method.
+ */
+ ret = VmbusChildDeviceAdd(newChannel->DeviceObject);
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS,
+ "unable to add child device object (relid %d)",
+ newChannel->OfferMsg.ChildRelId);
+
+ spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
+ list_del(&newChannel->ListEntry);
+ spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
+
+ FreeVmbusChannel(newChannel);
+ } else {
+ /*
+ * This state is used to indicate a successful open
+ * so that when we do close the channel normally, we
+ * can cleanup properly
+ */
+ newChannel->State = CHANNEL_OPEN_STATE;
+ }
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelProcessRescindOffer - Rescind the offer by initiating a device removal
+ */
+static void VmbusChannelProcessRescindOffer(void *context)
+{
+ struct vmbus_channel *channel = context;
+
+ DPRINT_ENTER(VMBUS);
+ VmbusChildDeviceRemove(channel->DeviceObject);
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelOnOffer - Handler for channel offers from vmbus in parent partition.
+ *
+ * We ignore all offers except network and storage offers. For each network and
+ * storage offers, we create a channel object and queue a work item to the
+ * channel object to process the offer synchronously
+ */
+static void VmbusChannelOnOffer(struct vmbus_channel_message_header *hdr)
+{
+ struct vmbus_channel_offer_channel *offer;
+ struct vmbus_channel *newChannel;
+ struct hv_guid *guidType;
+ struct hv_guid *guidInstance;
+ int i;
+ int fSupported = 0;
+
+ DPRINT_ENTER(VMBUS);
+
+ offer = (struct vmbus_channel_offer_channel *)hdr;
+ for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) {
+ if (memcmp(&offer->Offer.InterfaceType,
+ &gSupportedDeviceClasses[i], sizeof(struct hv_guid)) == 0) {
+ fSupported = 1;
+ break;
+ }
+ }
+
+ if (!fSupported) {
+ DPRINT_DBG(VMBUS, "Ignoring channel offer notification for "
+ "child relid %d", offer->ChildRelId);
+ DPRINT_EXIT(VMBUS);
+ return;
+ }
+
+ guidType = &offer->Offer.InterfaceType;
+ guidInstance = &offer->Offer.InterfaceInstance;
+
+ DPRINT_INFO(VMBUS, "Channel offer notification - "
+ "child relid %d monitor id %d allocated %d, "
+ "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x} "
+ "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x}",
+ offer->ChildRelId, offer->MonitorId,
+ offer->MonitorAllocated,
+ guidType->data[3], guidType->data[2],
+ guidType->data[1], guidType->data[0],
+ guidType->data[5], guidType->data[4],
+ guidType->data[7], guidType->data[6],
+ guidType->data[8], guidType->data[9],
+ guidType->data[10], guidType->data[11],
+ guidType->data[12], guidType->data[13],
+ guidType->data[14], guidType->data[15],
+ guidInstance->data[3], guidInstance->data[2],
+ guidInstance->data[1], guidInstance->data[0],
+ guidInstance->data[5], guidInstance->data[4],
+ guidInstance->data[7], guidInstance->data[6],
+ guidInstance->data[8], guidInstance->data[9],
+ guidInstance->data[10], guidInstance->data[11],
+ guidInstance->data[12], guidInstance->data[13],
+ guidInstance->data[14], guidInstance->data[15]);
+
+ /* Allocate the channel object and save this offer. */
+ newChannel = AllocVmbusChannel();
+ if (!newChannel) {
+ DPRINT_ERR(VMBUS, "unable to allocate channel object");
+ return;
+ }
+
+ DPRINT_DBG(VMBUS, "channel object allocated - %p", newChannel);
+
+ memcpy(&newChannel->OfferMsg, offer,
+ sizeof(struct vmbus_channel_offer_channel));
+ newChannel->MonitorGroup = (u8)offer->MonitorId / 32;
+ newChannel->MonitorBit = (u8)offer->MonitorId % 32;
+
+ /* TODO: Make sure the offer comes from our parent partition */
+ osd_schedule_callback(newChannel->ControlWQ, VmbusChannelProcessOffer,
+ newChannel);
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelOnOfferRescind - Rescind offer handler.
+ *
+ * We queue a work item to process this offer synchronously
+ */
+static void VmbusChannelOnOfferRescind(struct vmbus_channel_message_header *hdr)
+{
+ struct vmbus_channel_rescind_offer *rescind;
+ struct vmbus_channel *channel;
+
+ DPRINT_ENTER(VMBUS);
+
+ rescind = (struct vmbus_channel_rescind_offer *)hdr;
+ channel = GetChannelFromRelId(rescind->ChildRelId);
+ if (channel == NULL) {
+ DPRINT_DBG(VMBUS, "channel not found for relId %d",
+ rescind->ChildRelId);
+ return;
+ }
+
+ osd_schedule_callback(channel->ControlWQ,
+ VmbusChannelProcessRescindOffer,
+ channel);
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelOnOffersDelivered - This is invoked when all offers have been delivered.
+ *
+ * Nothing to do here.
+ */
+static void VmbusChannelOnOffersDelivered(
+ struct vmbus_channel_message_header *hdr)
+{
+ DPRINT_ENTER(VMBUS);
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelOnOpenResult - Open result handler.
+ *
+ * This is invoked when we received a response to our channel open request.
+ * Find the matching request, copy the response and signal the requesting
+ * thread.
+ */
+static void VmbusChannelOnOpenResult(struct vmbus_channel_message_header *hdr)
+{
+ struct vmbus_channel_open_result *result;
+ struct list_head *curr;
+ struct vmbus_channel_msginfo *msgInfo;
+ struct vmbus_channel_message_header *requestHeader;
+ struct vmbus_channel_open_channel *openMsg;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ result = (struct vmbus_channel_open_result *)hdr;
+ DPRINT_DBG(VMBUS, "vmbus open result - %d", result->Status);
+
+ /*
+ * Find the open msg, copy the result and signal/unblock the wait event
+ */
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+
+ list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
+/* FIXME: this should probably use list_entry() instead */
+ msgInfo = (struct vmbus_channel_msginfo *)curr;
+ requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
+
+ if (requestHeader->MessageType == ChannelMessageOpenChannel) {
+ openMsg = (struct vmbus_channel_open_channel *)msgInfo->Msg;
+ if (openMsg->ChildRelId == result->ChildRelId &&
+ openMsg->OpenId == result->OpenId) {
+ memcpy(&msgInfo->Response.OpenResult,
+ result,
+ sizeof(struct vmbus_channel_open_result));
+ osd_WaitEventSet(msgInfo->WaitEvent);
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelOnGpadlCreated - GPADL created handler.
+ *
+ * This is invoked when we received a response to our gpadl create request.
+ * Find the matching request, copy the response and signal the requesting
+ * thread.
+ */
+static void VmbusChannelOnGpadlCreated(struct vmbus_channel_message_header *hdr)
+{
+ struct vmbus_channel_gpadl_created *gpadlCreated;
+ struct list_head *curr;
+ struct vmbus_channel_msginfo *msgInfo;
+ struct vmbus_channel_message_header *requestHeader;
+ struct vmbus_channel_gpadl_header *gpadlHeader;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ gpadlCreated = (struct vmbus_channel_gpadl_created *)hdr;
+ DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d",
+ gpadlCreated->CreationStatus);
+
+ /*
+ * Find the establish msg, copy the result and signal/unblock the wait
+ * event
+ */
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+
+ list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
+/* FIXME: this should probably use list_entry() instead */
+ msgInfo = (struct vmbus_channel_msginfo *)curr;
+ requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
+
+ if (requestHeader->MessageType == ChannelMessageGpadlHeader) {
+ gpadlHeader = (struct vmbus_channel_gpadl_header *)requestHeader;
+
+ if ((gpadlCreated->ChildRelId ==
+ gpadlHeader->ChildRelId) &&
+ (gpadlCreated->Gpadl == gpadlHeader->Gpadl)) {
+ memcpy(&msgInfo->Response.GpadlCreated,
+ gpadlCreated,
+ sizeof(struct vmbus_channel_gpadl_created));
+ osd_WaitEventSet(msgInfo->WaitEvent);
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelOnGpadlTorndown - GPADL torndown handler.
+ *
+ * This is invoked when we received a response to our gpadl teardown request.
+ * Find the matching request, copy the response and signal the requesting
+ * thread.
+ */
+static void VmbusChannelOnGpadlTorndown(
+ struct vmbus_channel_message_header *hdr)
+{
+ struct vmbus_channel_gpadl_torndown *gpadlTorndown;
+ struct list_head *curr;
+ struct vmbus_channel_msginfo *msgInfo;
+ struct vmbus_channel_message_header *requestHeader;
+ struct vmbus_channel_gpadl_teardown *gpadlTeardown;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ gpadlTorndown = (struct vmbus_channel_gpadl_torndown *)hdr;
+
+ /*
+ * Find the open msg, copy the result and signal/unblock the wait event
+ */
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+
+ list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
+/* FIXME: this should probably use list_entry() instead */
+ msgInfo = (struct vmbus_channel_msginfo *)curr;
+ requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
+
+ if (requestHeader->MessageType == ChannelMessageGpadlTeardown) {
+ gpadlTeardown = (struct vmbus_channel_gpadl_teardown *)requestHeader;
+
+ if (gpadlTorndown->Gpadl == gpadlTeardown->Gpadl) {
+ memcpy(&msgInfo->Response.GpadlTorndown,
+ gpadlTorndown,
+ sizeof(struct vmbus_channel_gpadl_torndown));
+ osd_WaitEventSet(msgInfo->WaitEvent);
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelOnVersionResponse - Version response handler
+ *
+ * This is invoked when we received a response to our initiate contact request.
+ * Find the matching request, copy the response and signal the requesting
+ * thread.
+ */
+static void VmbusChannelOnVersionResponse(
+ struct vmbus_channel_message_header *hdr)
+{
+ struct list_head *curr;
+ struct vmbus_channel_msginfo *msgInfo;
+ struct vmbus_channel_message_header *requestHeader;
+ struct vmbus_channel_initiate_contact *initiate;
+ struct vmbus_channel_version_response *versionResponse;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ versionResponse = (struct vmbus_channel_version_response *)hdr;
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+
+ list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
+/* FIXME: this should probably use list_entry() instead */
+ msgInfo = (struct vmbus_channel_msginfo *)curr;
+ requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
+
+ if (requestHeader->MessageType ==
+ ChannelMessageInitiateContact) {
+ initiate = (struct vmbus_channel_initiate_contact *)requestHeader;
+ memcpy(&msgInfo->Response.VersionResponse,
+ versionResponse,
+ sizeof(struct vmbus_channel_version_response));
+ osd_WaitEventSet(msgInfo->WaitEvent);
+ }
+ }
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+}
+
+/* Channel message dispatch table */
+static struct vmbus_channel_message_table_entry
+ gChannelMessageTable[ChannelMessageCount] = {
+ {ChannelMessageInvalid, NULL},
+ {ChannelMessageOfferChannel, VmbusChannelOnOffer},
+ {ChannelMessageRescindChannelOffer, VmbusChannelOnOfferRescind},
+ {ChannelMessageRequestOffers, NULL},
+ {ChannelMessageAllOffersDelivered, VmbusChannelOnOffersDelivered},
+ {ChannelMessageOpenChannel, NULL},
+ {ChannelMessageOpenChannelResult, VmbusChannelOnOpenResult},
+ {ChannelMessageCloseChannel, NULL},
+ {ChannelMessageGpadlHeader, NULL},
+ {ChannelMessageGpadlBody, NULL},
+ {ChannelMessageGpadlCreated, VmbusChannelOnGpadlCreated},
+ {ChannelMessageGpadlTeardown, NULL},
+ {ChannelMessageGpadlTorndown, VmbusChannelOnGpadlTorndown},
+ {ChannelMessageRelIdReleased, NULL},
+ {ChannelMessageInitiateContact, NULL},
+ {ChannelMessageVersionResponse, VmbusChannelOnVersionResponse},
+ {ChannelMessageUnload, NULL},
+};
+
+/**
+ * VmbusOnChannelMessage - Handler for channel protocol messages.
+ *
+ * This is invoked in the vmbus worker thread context.
+ */
+void VmbusOnChannelMessage(void *Context)
+{
+ struct hv_message *msg = Context;
+ struct vmbus_channel_message_header *hdr;
+ int size;
+
+ DPRINT_ENTER(VMBUS);
+
+ hdr = (struct vmbus_channel_message_header *)msg->u.Payload;
+ size = msg->Header.PayloadSize;
+
+ DPRINT_DBG(VMBUS, "message type %d size %d", hdr->MessageType, size);
+
+ if (hdr->MessageType >= ChannelMessageCount) {
+ DPRINT_ERR(VMBUS,
+ "Received invalid channel message type %d size %d",
+ hdr->MessageType, size);
+ print_hex_dump_bytes("", DUMP_PREFIX_NONE,
+ (unsigned char *)msg->u.Payload, size);
+ kfree(msg);
+ return;
+ }
+
+ if (gChannelMessageTable[hdr->MessageType].messageHandler)
+ gChannelMessageTable[hdr->MessageType].messageHandler(hdr);
+ else
+ DPRINT_ERR(VMBUS, "Unhandled channel message type %d",
+ hdr->MessageType);
+
+ /* Free the msg that was allocated in VmbusOnMsgDPC() */
+ kfree(msg);
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusChannelRequestOffers - Send a request to get all our pending offers.
+ */
+int VmbusChannelRequestOffers(void)
+{
+ struct vmbus_channel_message_header *msg;
+ struct vmbus_channel_msginfo *msgInfo;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+
+ msgInfo = kmalloc(sizeof(*msgInfo) +
+ sizeof(struct vmbus_channel_message_header),
+ GFP_KERNEL);
+ ASSERT(msgInfo != NULL);
+
+ msgInfo->WaitEvent = osd_WaitEventCreate();
+ msg = (struct vmbus_channel_message_header *)msgInfo->Msg;
+
+ msg->MessageType = ChannelMessageRequestOffers;
+
+ /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
+ INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList,
+ &msgInfo->msgListEntry);
+ SpinlockRelease(gVmbusConnection.channelMsgLock);*/
+
+ ret = VmbusPostMessage(msg,
+ sizeof(struct vmbus_channel_message_header));
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS, "Unable to request offers - %d", ret);
+
+ /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
+ REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
+ SpinlockRelease(gVmbusConnection.channelMsgLock);*/
+
+ goto Cleanup;
+ }
+ /* osd_WaitEventWait(msgInfo->waitEvent); */
+
+ /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
+ REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
+ SpinlockRelease(gVmbusConnection.channelMsgLock);*/
+
+
+Cleanup:
+ if (msgInfo) {
+ kfree(msgInfo->WaitEvent);
+ kfree(msgInfo);
+ }
+
+ DPRINT_EXIT(VMBUS);
+ return ret;
+}
+
+/**
+ * VmbusChannelReleaseUnattachedChannels - Release channels that are unattached/unconnected ie (no drivers associated)
+ */
+void VmbusChannelReleaseUnattachedChannels(void)
+{
+ struct vmbus_channel *channel, *pos;
+ struct vmbus_channel *start = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
+
+ list_for_each_entry_safe(channel, pos, &gVmbusConnection.ChannelList,
+ ListEntry) {
+ if (channel == start)
+ break;
+
+ if (!channel->DeviceObject->Driver) {
+ list_del(&channel->ListEntry);
+ DPRINT_INFO(VMBUS,
+ "Releasing unattached device object %p",
+ channel->DeviceObject);
+
+ VmbusChildDeviceRemove(channel->DeviceObject);
+ FreeVmbusChannel(channel);
+ } else {
+ if (!start)
+ start = channel;
+ }
+ }
+
+ spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
+}
+
+/* eof */
diff --git a/drivers/staging/hv/ChannelMgmt.h b/drivers/staging/hv/ChannelMgmt.h
new file mode 100644
index 000000000000..a839d8fe6cec
--- /dev/null
+++ b/drivers/staging/hv/ChannelMgmt.h
@@ -0,0 +1,319 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _CHANNEL_MGMT_H_
+#define _CHANNEL_MGMT_H_
+
+#include <linux/list.h>
+#include "RingBuffer.h"
+#include "VmbusChannelInterface.h"
+#include "VmbusPacketFormat.h"
+
+/* Version 1 messages */
+enum vmbus_channel_message_type {
+ ChannelMessageInvalid = 0,
+ ChannelMessageOfferChannel = 1,
+ ChannelMessageRescindChannelOffer = 2,
+ ChannelMessageRequestOffers = 3,
+ ChannelMessageAllOffersDelivered = 4,
+ ChannelMessageOpenChannel = 5,
+ ChannelMessageOpenChannelResult = 6,
+ ChannelMessageCloseChannel = 7,
+ ChannelMessageGpadlHeader = 8,
+ ChannelMessageGpadlBody = 9,
+ ChannelMessageGpadlCreated = 10,
+ ChannelMessageGpadlTeardown = 11,
+ ChannelMessageGpadlTorndown = 12,
+ ChannelMessageRelIdReleased = 13,
+ ChannelMessageInitiateContact = 14,
+ ChannelMessageVersionResponse = 15,
+ ChannelMessageUnload = 16,
+#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD
+ ChannelMessageViewRangeAdd = 17,
+ ChannelMessageViewRangeRemove = 18,
+#endif
+ ChannelMessageCount
+} __attribute__((packed));
+
+struct vmbus_channel_message_header {
+ enum vmbus_channel_message_type MessageType;
+ u32 Padding;
+} __attribute__((packed));
+
+/* Query VMBus Version parameters */
+struct vmbus_channel_query_vmbus_version {
+ struct vmbus_channel_message_header Header;
+ u32 Version;
+} __attribute__((packed));
+
+/* VMBus Version Supported parameters */
+struct vmbus_channel_version_supported {
+ struct vmbus_channel_message_header Header;
+ bool VersionSupported;
+} __attribute__((packed));
+
+/* Offer Channel parameters */
+struct vmbus_channel_offer_channel {
+ struct vmbus_channel_message_header Header;
+ struct vmbus_channel_offer Offer;
+ u32 ChildRelId;
+ u8 MonitorId;
+ bool MonitorAllocated;
+} __attribute__((packed));
+
+/* Rescind Offer parameters */
+struct vmbus_channel_rescind_offer {
+ struct vmbus_channel_message_header Header;
+ u32 ChildRelId;
+} __attribute__((packed));
+
+/*
+ * Request Offer -- no parameters, SynIC message contains the partition ID
+ * Set Snoop -- no parameters, SynIC message contains the partition ID
+ * Clear Snoop -- no parameters, SynIC message contains the partition ID
+ * All Offers Delivered -- no parameters, SynIC message contains the partition
+ * ID
+ * Flush Client -- no parameters, SynIC message contains the partition ID
+ */
+
+/* Open Channel parameters */
+struct vmbus_channel_open_channel {
+ struct vmbus_channel_message_header Header;
+
+ /* Identifies the specific VMBus channel that is being opened. */
+ u32 ChildRelId;
+
+ /* ID making a particular open request at a channel offer unique. */
+ u32 OpenId;
+
+ /* GPADL for the channel's ring buffer. */
+ u32 RingBufferGpadlHandle;
+
+ /* GPADL for the channel's server context save area. */
+ u32 ServerContextAreaGpadlHandle;
+
+ /*
+ * The upstream ring buffer begins at offset zero in the memory
+ * described by RingBufferGpadlHandle. The downstream ring buffer
+ * follows it at this offset (in pages).
+ */
+ u32 DownstreamRingBufferPageOffset;
+
+ /* User-specific data to be passed along to the server endpoint. */
+ unsigned char UserData[MAX_USER_DEFINED_BYTES];
+} __attribute__((packed));
+
+/* Open Channel Result parameters */
+struct vmbus_channel_open_result {
+ struct vmbus_channel_message_header Header;
+ u32 ChildRelId;
+ u32 OpenId;
+ u32 Status;
+} __attribute__((packed));
+
+/* Close channel parameters; */
+struct vmbus_channel_close_channel {
+ struct vmbus_channel_message_header Header;
+ u32 ChildRelId;
+} __attribute__((packed));
+
+/* Channel Message GPADL */
+#define GPADL_TYPE_RING_BUFFER 1
+#define GPADL_TYPE_SERVER_SAVE_AREA 2
+#define GPADL_TYPE_TRANSACTION 8
+
+/*
+ * The number of PFNs in a GPADL message is defined by the number of
+ * pages that would be spanned by ByteCount and ByteOffset. If the
+ * implied number of PFNs won't fit in this packet, there will be a
+ * follow-up packet that contains more.
+ */
+struct vmbus_channel_gpadl_header {
+ struct vmbus_channel_message_header Header;
+ u32 ChildRelId;
+ u32 Gpadl;
+ u16 RangeBufLen;
+ u16 RangeCount;
+ struct gpa_range Range[0];
+} __attribute__((packed));
+
+/* This is the followup packet that contains more PFNs. */
+struct vmbus_channel_gpadl_body {
+ struct vmbus_channel_message_header Header;
+ u32 MessageNumber;
+ u32 Gpadl;
+ u64 Pfn[0];
+} __attribute__((packed));
+
+struct vmbus_channel_gpadl_created {
+ struct vmbus_channel_message_header Header;
+ u32 ChildRelId;
+ u32 Gpadl;
+ u32 CreationStatus;
+} __attribute__((packed));
+
+struct vmbus_channel_gpadl_teardown {
+ struct vmbus_channel_message_header Header;
+ u32 ChildRelId;
+ u32 Gpadl;
+} __attribute__((packed));
+
+struct vmbus_channel_gpadl_torndown {
+ struct vmbus_channel_message_header Header;
+ u32 Gpadl;
+} __attribute__((packed));
+
+#ifdef VMBUS_FEATURE_PARENT_OR_PEER_MEMORY_MAPPED_INTO_A_CHILD
+struct vmbus_channel_view_range_add {
+ struct vmbus_channel_message_header Header;
+ PHYSICAL_ADDRESS ViewRangeBase;
+ u64 ViewRangeLength;
+ u32 ChildRelId;
+} __attribute__((packed));
+
+struct vmbus_channel_view_range_remove {
+ struct vmbus_channel_message_header Header;
+ PHYSICAL_ADDRESS ViewRangeBase;
+ u32 ChildRelId;
+} __attribute__((packed));
+#endif
+
+struct vmbus_channel_relid_released {
+ struct vmbus_channel_message_header Header;
+ u32 ChildRelId;
+} __attribute__((packed));
+
+struct vmbus_channel_initiate_contact {
+ struct vmbus_channel_message_header Header;
+ u32 VMBusVersionRequested;
+ u32 Padding2;
+ u64 InterruptPage;
+ u64 MonitorPage1;
+ u64 MonitorPage2;
+} __attribute__((packed));
+
+struct vmbus_channel_version_response {
+ struct vmbus_channel_message_header Header;
+ bool VersionSupported;
+} __attribute__((packed));
+
+enum vmbus_channel_state {
+ CHANNEL_OFFER_STATE,
+ CHANNEL_OPENING_STATE,
+ CHANNEL_OPEN_STATE,
+};
+
+struct vmbus_channel {
+ struct list_head ListEntry;
+
+ struct hv_device *DeviceObject;
+
+ struct timer_list poll_timer; /* SA-111 workaround */
+
+ enum vmbus_channel_state State;
+
+ struct vmbus_channel_offer_channel OfferMsg;
+ /*
+ * These are based on the OfferMsg.MonitorId.
+ * Save it here for easy access.
+ */
+ u8 MonitorGroup;
+ u8 MonitorBit;
+
+ u32 RingBufferGpadlHandle;
+
+ /* Allocated memory for ring buffer */
+ void *RingBufferPages;
+ u32 RingBufferPageCount;
+ RING_BUFFER_INFO Outbound; /* send to parent */
+ RING_BUFFER_INFO Inbound; /* receive from parent */
+ spinlock_t inbound_lock;
+ struct workqueue_struct *ControlWQ;
+
+ /* Channel callback are invoked in this workqueue context */
+ /* HANDLE dataWorkQueue; */
+
+ void (*OnChannelCallback)(void *context);
+ void *ChannelCallbackContext;
+};
+
+struct vmbus_channel_debug_info {
+ u32 RelId;
+ enum vmbus_channel_state State;
+ struct hv_guid InterfaceType;
+ struct hv_guid InterfaceInstance;
+ u32 MonitorId;
+ u32 ServerMonitorPending;
+ u32 ServerMonitorLatency;
+ u32 ServerMonitorConnectionId;
+ u32 ClientMonitorPending;
+ u32 ClientMonitorLatency;
+ u32 ClientMonitorConnectionId;
+
+ RING_BUFFER_DEBUG_INFO Inbound;
+ RING_BUFFER_DEBUG_INFO Outbound;
+};
+
+/*
+ * Represents each channel msg on the vmbus connection This is a
+ * variable-size data structure depending on the msg type itself
+ */
+struct vmbus_channel_msginfo {
+ /* Bookkeeping stuff */
+ struct list_head MsgListEntry;
+
+ /* So far, this is only used to handle gpadl body message */
+ struct list_head SubMsgList;
+
+ /* Synchronize the request/response if needed */
+ struct osd_waitevent *WaitEvent;
+
+ union {
+ struct vmbus_channel_version_supported VersionSupported;
+ struct vmbus_channel_open_result OpenResult;
+ struct vmbus_channel_gpadl_torndown GpadlTorndown;
+ struct vmbus_channel_gpadl_created GpadlCreated;
+ struct vmbus_channel_version_response VersionResponse;
+ } Response;
+
+ u32 MessageSize;
+ /*
+ * The channel message that goes out on the "wire".
+ * It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header
+ */
+ unsigned char Msg[0];
+};
+
+
+struct vmbus_channel *AllocVmbusChannel(void);
+
+void FreeVmbusChannel(struct vmbus_channel *Channel);
+
+void VmbusOnChannelMessage(void *Context);
+
+int VmbusChannelRequestOffers(void);
+
+void VmbusChannelReleaseUnattachedChannels(void);
+
+#endif /* _CHANNEL_MGMT_H_ */
diff --git a/drivers/staging/hv/Connection.c b/drivers/staging/hv/Connection.c
new file mode 100644
index 000000000000..43c2e6855015
--- /dev/null
+++ b/drivers/staging/hv/Connection.c
@@ -0,0 +1,341 @@
+/*
+ *
+ * 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/mm.h>
+#include <linux/vmalloc.h>
+#include "osd.h"
+#include "logging.h"
+#include "VmbusPrivate.h"
+
+
+struct VMBUS_CONNECTION gVmbusConnection = {
+ .ConnectState = Disconnected,
+ .NextGpadlHandle = ATOMIC_INIT(0xE1E10),
+};
+
+/**
+ * VmbusConnect - Sends a connect request on the partition service connection
+ */
+int VmbusConnect(void)
+{
+ int ret = 0;
+ struct vmbus_channel_msginfo *msgInfo = NULL;
+ struct vmbus_channel_initiate_contact *msg;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Make sure we are not connecting or connected */
+ if (gVmbusConnection.ConnectState != Disconnected)
+ return -1;
+
+ /* Initialize the vmbus connection */
+ gVmbusConnection.ConnectState = Connecting;
+ gVmbusConnection.WorkQueue = create_workqueue("hv_vmbus_con");
+ if (!gVmbusConnection.WorkQueue) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ INIT_LIST_HEAD(&gVmbusConnection.ChannelMsgList);
+ spin_lock_init(&gVmbusConnection.channelmsg_lock);
+
+ INIT_LIST_HEAD(&gVmbusConnection.ChannelList);
+ spin_lock_init(&gVmbusConnection.channel_lock);
+
+ /*
+ * Setup the vmbus event connection for channel interrupt
+ * abstraction stuff
+ */
+ gVmbusConnection.InterruptPage = osd_PageAlloc(1);
+ if (gVmbusConnection.InterruptPage == NULL) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage;
+ gVmbusConnection.SendInterruptPage =
+ (void *)((unsigned long)gVmbusConnection.InterruptPage +
+ (PAGE_SIZE >> 1));
+
+ /*
+ * Setup the monitor notification facility. The 1st page for
+ * parent->child and the 2nd page for child->parent
+ */
+ gVmbusConnection.MonitorPages = osd_PageAlloc(2);
+ if (gVmbusConnection.MonitorPages == NULL) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ msgInfo = kzalloc(sizeof(*msgInfo) +
+ sizeof(struct vmbus_channel_initiate_contact),
+ GFP_KERNEL);
+ if (msgInfo == NULL) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ msgInfo->WaitEvent = osd_WaitEventCreate();
+ msg = (struct vmbus_channel_initiate_contact *)msgInfo->Msg;
+
+ msg->Header.MessageType = ChannelMessageInitiateContact;
+ msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER;
+ msg->InterruptPage = virt_to_phys(gVmbusConnection.InterruptPage);
+ msg->MonitorPage1 = virt_to_phys(gVmbusConnection.MonitorPages);
+ msg->MonitorPage2 = virt_to_phys(
+ (void *)((unsigned long)gVmbusConnection.MonitorPages +
+ PAGE_SIZE));
+
+ /*
+ * Add to list before we send the request since we may
+ * receive the response before returning from this routine
+ */
+ spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
+ list_add_tail(&msgInfo->MsgListEntry,
+ &gVmbusConnection.ChannelMsgList);
+
+ spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
+
+ DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, "
+ "monitor1 pfn %llx,, monitor2 pfn %llx",
+ msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2);
+
+ DPRINT_DBG(VMBUS, "Sending channel initiate msg...");
+ ret = VmbusPostMessage(msg,
+ sizeof(struct vmbus_channel_initiate_contact));
+ if (ret != 0) {
+ list_del(&msgInfo->MsgListEntry);
+ goto Cleanup;
+ }
+
+ /* Wait for the connection response */
+ osd_WaitEventWait(msgInfo->WaitEvent);
+
+ list_del(&msgInfo->MsgListEntry);
+
+ /* Check if successful */
+ if (msgInfo->Response.VersionResponse.VersionSupported) {
+ DPRINT_INFO(VMBUS, "Vmbus connected!!");
+ gVmbusConnection.ConnectState = Connected;
+
+ } else {
+ DPRINT_ERR(VMBUS, "Vmbus connection failed!!..."
+ "current version (%d) not supported",
+ VMBUS_REVISION_NUMBER);
+ ret = -1;
+ goto Cleanup;
+ }
+
+ kfree(msgInfo->WaitEvent);
+ kfree(msgInfo);
+ DPRINT_EXIT(VMBUS);
+
+ return 0;
+
+Cleanup:
+ gVmbusConnection.ConnectState = Disconnected;
+
+ if (gVmbusConnection.WorkQueue)
+ destroy_workqueue(gVmbusConnection.WorkQueue);
+
+ if (gVmbusConnection.InterruptPage) {
+ osd_PageFree(gVmbusConnection.InterruptPage, 1);
+ gVmbusConnection.InterruptPage = NULL;
+ }
+
+ if (gVmbusConnection.MonitorPages) {
+ osd_PageFree(gVmbusConnection.MonitorPages, 2);
+ gVmbusConnection.MonitorPages = NULL;
+ }
+
+ if (msgInfo) {
+ kfree(msgInfo->WaitEvent);
+ kfree(msgInfo);
+ }
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusDisconnect - Sends a disconnect request on the partition service connection
+ */
+int VmbusDisconnect(void)
+{
+ int ret = 0;
+ struct vmbus_channel_message_header *msg;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Make sure we are connected */
+ if (gVmbusConnection.ConnectState != Connected)
+ return -1;
+
+ msg = kzalloc(sizeof(struct vmbus_channel_message_header), GFP_KERNEL);
+
+ msg->MessageType = ChannelMessageUnload;
+
+ ret = VmbusPostMessage(msg,
+ sizeof(struct vmbus_channel_message_header));
+ if (ret != 0)
+ goto Cleanup;
+
+ osd_PageFree(gVmbusConnection.InterruptPage, 1);
+
+ /* TODO: iterate thru the msg list and free up */
+ destroy_workqueue(gVmbusConnection.WorkQueue);
+
+ gVmbusConnection.ConnectState = Disconnected;
+
+ DPRINT_INFO(VMBUS, "Vmbus disconnected!!");
+
+Cleanup:
+ kfree(msg);
+ DPRINT_EXIT(VMBUS);
+ return ret;
+}
+
+/**
+ * GetChannelFromRelId - Get the channel object given its child relative id (ie channel id)
+ */
+struct vmbus_channel *GetChannelFromRelId(u32 relId)
+{
+ struct vmbus_channel *channel;
+ struct vmbus_channel *foundChannel = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
+ list_for_each_entry(channel, &gVmbusConnection.ChannelList, ListEntry) {
+ if (channel->OfferMsg.ChildRelId == relId) {
+ foundChannel = channel;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
+
+ return foundChannel;
+}
+
+/**
+ * VmbusProcessChannelEvent - Process a channel event notification
+ */
+static void VmbusProcessChannelEvent(void *context)
+{
+ struct vmbus_channel *channel;
+ u32 relId = (u32)(unsigned long)context;
+
+ ASSERT(relId > 0);
+
+ /*
+ * Find the channel based on this relid and invokes the
+ * channel callback to process the event
+ */
+ channel = GetChannelFromRelId(relId);
+
+ if (channel) {
+ VmbusChannelOnChannelEvent(channel);
+ /*
+ * WorkQueueQueueWorkItem(channel->dataWorkQueue,
+ * VmbusChannelOnChannelEvent,
+ * (void*)channel);
+ */
+ } else {
+ DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId);
+ }
+}
+
+/**
+ * VmbusOnEvents - Handler for events
+ */
+void VmbusOnEvents(void)
+{
+ int dword;
+ int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
+ int bit;
+ int relid;
+ u32 *recvInterruptPage = gVmbusConnection.RecvInterruptPage;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Check events */
+ if (recvInterruptPage) {
+ for (dword = 0; dword < maxdword; dword++) {
+ if (recvInterruptPage[dword]) {
+ for (bit = 0; bit < 32; bit++) {
+ if (test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) {
+ relid = (dword << 5) + bit;
+ DPRINT_DBG(VMBUS, "event detected for relid - %d", relid);
+
+ if (relid == 0) {
+ /* special case - vmbus channel protocol msg */
+ DPRINT_DBG(VMBUS, "invalid relid - %d", relid);
+ continue;
+ } else {
+ /* QueueWorkItem(VmbusProcessEvent, (void*)relid); */
+ /* ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid); */
+ VmbusProcessChannelEvent((void *)(unsigned long)relid);
+ }
+ }
+ }
+ }
+ }
+ }
+ DPRINT_EXIT(VMBUS);
+
+ return;
+}
+
+/**
+ * VmbusPostMessage - Send a msg on the vmbus's message connection
+ */
+int VmbusPostMessage(void *buffer, size_t bufferLen)
+{
+ union hv_connection_id connId;
+
+ connId.Asu32 = 0;
+ connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID;
+ return HvPostMessage(connId, 1, buffer, bufferLen);
+}
+
+/**
+ * VmbusSetEvent - Send an event notification to the parent
+ */
+int VmbusSetEvent(u32 childRelId)
+{
+ int ret = 0;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Each u32 represents 32 channels */
+ set_bit(childRelId & 31,
+ (unsigned long *)gVmbusConnection.SendInterruptPage +
+ (childRelId >> 5));
+
+ ret = HvSignalEvent();
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
diff --git a/drivers/staging/hv/Hv.c b/drivers/staging/hv/Hv.c
new file mode 100644
index 000000000000..c5b6613f2f2f
--- /dev/null
+++ b/drivers/staging/hv/Hv.c
@@ -0,0 +1,568 @@
+/*
+ * 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/mm.h>
+#include <linux/vmalloc.h>
+#include "osd.h"
+#include "logging.h"
+#include "VmbusPrivate.h"
+
+/* The one and only */
+struct hv_context gHvContext = {
+ .SynICInitialized = false,
+ .HypercallPage = NULL,
+ .SignalEventParam = NULL,
+ .SignalEventBuffer = NULL,
+};
+
+/**
+ * HvQueryHypervisorPresence - Query the cpuid for presense of windows hypervisor
+ */
+static int HvQueryHypervisorPresence(void)
+{
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+ unsigned int op;
+
+ eax = 0;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ op = HvCpuIdFunctionVersionAndFeatures;
+ cpuid(op, &eax, &ebx, &ecx, &edx);
+
+ return ecx & HV_PRESENT_BIT;
+}
+
+/**
+ * HvQueryHypervisorInfo - Get version info of the windows hypervisor
+ */
+static int HvQueryHypervisorInfo(void)
+{
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+ unsigned int maxLeaf;
+ unsigned int op;
+
+ /*
+ * Its assumed that this is called after confirming that Viridian
+ * is present. Query id and revision.
+ */
+ eax = 0;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ op = HvCpuIdFunctionHvVendorAndMaxFunction;
+ cpuid(op, &eax, &ebx, &ecx, &edx);
+
+ DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
+ (ebx & 0xFF),
+ ((ebx >> 8) & 0xFF),
+ ((ebx >> 16) & 0xFF),
+ ((ebx >> 24) & 0xFF),
+ (ecx & 0xFF),
+ ((ecx >> 8) & 0xFF),
+ ((ecx >> 16) & 0xFF),
+ ((ecx >> 24) & 0xFF),
+ (edx & 0xFF),
+ ((edx >> 8) & 0xFF),
+ ((edx >> 16) & 0xFF),
+ ((edx >> 24) & 0xFF));
+
+ maxLeaf = eax;
+ eax = 0;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ op = HvCpuIdFunctionHvInterface;
+ cpuid(op, &eax, &ebx, &ecx, &edx);
+
+ DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
+ (eax & 0xFF),
+ ((eax >> 8) & 0xFF),
+ ((eax >> 16) & 0xFF),
+ ((eax >> 24) & 0xFF));
+
+ if (maxLeaf >= HvCpuIdFunctionMsHvVersion) {
+ eax = 0;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ op = HvCpuIdFunctionMsHvVersion;
+ cpuid(op, &eax, &ebx, &ecx, &edx);
+ DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",\
+ eax,
+ ebx >> 16,
+ ebx & 0xFFFF,
+ ecx,
+ edx >> 24,
+ edx & 0xFFFFFF);
+ }
+ return maxLeaf;
+}
+
+/**
+ * HvDoHypercall - Invoke the specified hypercall
+ */
+static u64 HvDoHypercall(u64 Control, void *Input, void *Output)
+{
+#ifdef CONFIG_X86_64
+ u64 hvStatus = 0;
+ u64 inputAddress = (Input) ? virt_to_phys(Input) : 0;
+ u64 outputAddress = (Output) ? virt_to_phys(Output) : 0;
+ volatile void *hypercallPage = gHvContext.HypercallPage;
+
+ DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p "
+ "output phys %llx virt %p hypercall %p>",
+ Control, inputAddress, Input,
+ outputAddress, Output, hypercallPage);
+
+ __asm__ __volatile__("mov %0, %%r8" : : "r" (outputAddress) : "r8");
+ __asm__ __volatile__("call *%3" : "=a" (hvStatus) :
+ "c" (Control), "d" (inputAddress),
+ "m" (hypercallPage));
+
+ DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus);
+
+ return hvStatus;
+
+#else
+
+ u32 controlHi = Control >> 32;
+ u32 controlLo = Control & 0xFFFFFFFF;
+ u32 hvStatusHi = 1;
+ u32 hvStatusLo = 1;
+ u64 inputAddress = (Input) ? virt_to_phys(Input) : 0;
+ u32 inputAddressHi = inputAddress >> 32;
+ u32 inputAddressLo = inputAddress & 0xFFFFFFFF;
+ u64 outputAddress = (Output) ? virt_to_phys(Output) : 0;
+ u32 outputAddressHi = outputAddress >> 32;
+ u32 outputAddressLo = outputAddress & 0xFFFFFFFF;
+ volatile void *hypercallPage = gHvContext.HypercallPage;
+
+ DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
+ Control, Input, Output);
+
+ __asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi),
+ "=a"(hvStatusLo) : "d" (controlHi),
+ "a" (controlLo), "b" (inputAddressHi),
+ "c" (inputAddressLo), "D"(outputAddressHi),
+ "S"(outputAddressLo), "m" (hypercallPage));
+
+ DPRINT_DBG(VMBUS, "Hypercall <return %llx>",
+ hvStatusLo | ((u64)hvStatusHi << 32));
+
+ return hvStatusLo | ((u64)hvStatusHi << 32);
+#endif /* !x86_64 */
+}
+
+/**
+ * HvInit - Main initialization routine.
+ *
+ * This routine must be called before any other routines in here are called
+ */
+int HvInit(void)
+{
+ int ret = 0;
+ int maxLeaf;
+ union hv_x64_msr_hypercall_contents hypercallMsr;
+ void *virtAddr = NULL;
+
+ DPRINT_ENTER(VMBUS);
+
+ memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS);
+ memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS);
+
+ if (!HvQueryHypervisorPresence()) {
+ DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
+ goto Cleanup;
+ }
+
+ DPRINT_INFO(VMBUS,
+ "Windows hypervisor detected! Retrieving more info...");
+
+ maxLeaf = HvQueryHypervisorInfo();
+ /* HvQueryHypervisorFeatures(maxLeaf); */
+
+ /*
+ * Determine if we are running on xenlinux (ie x2v shim) or native
+ * linux
+ */
+ rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId);
+ if (gHvContext.GuestId == 0) {
+ /* Write our OS info */
+ wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
+ gHvContext.GuestId = HV_LINUX_GUEST_ID;
+ }
+
+ /* See if the hypercall page is already set */
+ rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+ if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
+ /* Allocate the hypercall page memory */
+ /* virtAddr = osd_PageAlloc(1); */
+ virtAddr = osd_VirtualAllocExec(PAGE_SIZE);
+
+ if (!virtAddr) {
+ DPRINT_ERR(VMBUS,
+ "unable to allocate hypercall page!!");
+ goto Cleanup;
+ }
+
+ hypercallMsr.Enable = 1;
+ /* hypercallMsr.GuestPhysicalAddress =
+ * virt_to_phys(virtAddr) >> PAGE_SHIFT; */
+ hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr);
+ wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+
+ /* Confirm that hypercall page did get setup. */
+ hypercallMsr.AsUINT64 = 0;
+ rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+ if (!hypercallMsr.Enable) {
+ DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
+ goto Cleanup;
+ }
+
+ gHvContext.HypercallPage = virtAddr;
+ } else {
+ DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!",
+ gHvContext.GuestId);
+ goto Cleanup;
+ }
+
+ DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
+ gHvContext.HypercallPage,
+ (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
+
+ /* Setup the global signal event param for the signal event hypercall */
+ gHvContext.SignalEventBuffer =
+ kmalloc(sizeof(struct hv_input_signal_event_buffer),
+ GFP_KERNEL);
+ if (!gHvContext.SignalEventBuffer)
+ goto Cleanup;
+
+ gHvContext.SignalEventParam =
+ (struct hv_input_signal_event *)
+ (ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer,
+ HV_HYPERCALL_PARAM_ALIGN));
+ gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
+ gHvContext.SignalEventParam->ConnectionId.u.Id =
+ VMBUS_EVENT_CONNECTION_ID;
+ gHvContext.SignalEventParam->FlagNumber = 0;
+ gHvContext.SignalEventParam->RsvdZ = 0;
+
+ /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+
+Cleanup:
+ if (virtAddr) {
+ if (hypercallMsr.Enable) {
+ hypercallMsr.AsUINT64 = 0;
+ wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+ }
+
+ vfree(virtAddr);
+ }
+ ret = -1;
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * HvCleanup - Cleanup routine.
+ *
+ * This routine is called normally during driver unloading or exiting.
+ */
+void HvCleanup(void)
+{
+ union hv_x64_msr_hypercall_contents hypercallMsr;
+
+ DPRINT_ENTER(VMBUS);
+
+ if (gHvContext.SignalEventBuffer) {
+ gHvContext.SignalEventBuffer = NULL;
+ gHvContext.SignalEventParam = NULL;
+ kfree(gHvContext.SignalEventBuffer);
+ }
+
+ if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
+ if (gHvContext.HypercallPage) {
+ hypercallMsr.AsUINT64 = 0;
+ wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+ vfree(gHvContext.HypercallPage);
+ gHvContext.HypercallPage = NULL;
+ }
+ }
+
+ DPRINT_EXIT(VMBUS);
+
+}
+
+/**
+ * HvPostMessage - Post a message using the hypervisor message IPC.
+ *
+ * This involves a hypercall.
+ */
+u16 HvPostMessage(union hv_connection_id connectionId,
+ enum hv_message_type messageType,
+ void *payload, size_t payloadSize)
+{
+ struct alignedInput {
+ u64 alignment8;
+ struct hv_input_post_message msg;
+ };
+
+ struct hv_input_post_message *alignedMsg;
+ u16 status;
+ unsigned long addr;
+
+ if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
+ return -1;
+
+ addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
+ if (!addr)
+ return -1;
+
+ alignedMsg = (struct hv_input_post_message *)
+ (ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
+
+ alignedMsg->ConnectionId = connectionId;
+ alignedMsg->MessageType = messageType;
+ alignedMsg->PayloadSize = payloadSize;
+ memcpy((void *)alignedMsg->Payload, payload, payloadSize);
+
+ status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
+
+ kfree((void *)addr);
+
+ return status;
+}
+
+
+/**
+ * HvSignalEvent - Signal an event on the specified connection using the hypervisor event IPC.
+ *
+ * This involves a hypercall.
+ */
+u16 HvSignalEvent(void)
+{
+ u16 status;
+
+ status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam,
+ NULL) & 0xFFFF;
+ return status;
+}
+
+/**
+ * HvSynicInit - Initialize the Synthethic Interrupt Controller.
+ *
+ * If it is already initialized by another entity (ie x2v shim), we need to
+ * retrieve the initialized message and event pages. Otherwise, we create and
+ * initialize the message and event pages.
+ */
+int HvSynicInit(u32 irqVector)
+{
+ u64 version;
+ union hv_synic_simp simp;
+ union hv_synic_siefp siefp;
+ union hv_synic_sint sharedSint;
+ union hv_synic_scontrol sctrl;
+ u64 guestID;
+ int ret = 0;
+
+ DPRINT_ENTER(VMBUS);
+
+ if (!gHvContext.HypercallPage) {
+ DPRINT_EXIT(VMBUS);
+ return ret;
+ }
+
+ /* Check the version */
+ rdmsrl(HV_X64_MSR_SVERSION, version);
+
+ DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
+
+ /* TODO: Handle SMP */
+ if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID) {
+ DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since "
+ "it is already set.");
+
+ rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
+ rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
+
+ DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx",
+ simp.AsUINT64, siefp.AsUINT64);
+
+ /*
+ * Determine if we are running on xenlinux (ie x2v shim) or
+ * native linux
+ */
+ rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID);
+ if (guestID == HV_LINUX_GUEST_ID) {
+ gHvContext.synICMessagePage[0] =
+ phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
+ gHvContext.synICEventPage[0] =
+ phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
+ } else {
+ DPRINT_ERR(VMBUS, "unknown guest id!!");
+ goto Cleanup;
+ }
+ DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p",
+ gHvContext.synICMessagePage[0],
+ gHvContext.synICEventPage[0]);
+ } else {
+ gHvContext.synICMessagePage[0] = osd_PageAlloc(1);
+ if (gHvContext.synICMessagePage[0] == NULL) {
+ DPRINT_ERR(VMBUS,
+ "unable to allocate SYNIC message page!!");
+ goto Cleanup;
+ }
+
+ gHvContext.synICEventPage[0] = osd_PageAlloc(1);
+ if (gHvContext.synICEventPage[0] == NULL) {
+ DPRINT_ERR(VMBUS,
+ "unable to allocate SYNIC event page!!");
+ goto Cleanup;
+ }
+
+ /* Setup the Synic's message page */
+ rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
+ simp.SimpEnabled = 1;
+ simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0])
+ >> PAGE_SHIFT;
+
+ DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx",
+ simp.AsUINT64);
+
+ wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
+
+ /* Setup the Synic's event page */
+ rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
+ siefp.SiefpEnabled = 1;
+ siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0])
+ >> PAGE_SHIFT;
+
+ DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx",
+ siefp.AsUINT64);
+
+ wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
+ }
+
+ /* Setup the interception SINT. */
+ /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
+ /* interceptionSint.AsUINT64); */
+
+ /* Setup the shared SINT. */
+ rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
+
+ sharedSint.AsUINT64 = 0;
+ sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
+ sharedSint.Masked = false;
+ sharedSint.AutoEoi = true;
+
+ DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx",
+ sharedSint.AsUINT64);
+
+ wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
+
+ /* Enable the global synic bit */
+ rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
+ sctrl.Enable = 1;
+
+ wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
+
+ gHvContext.SynICInitialized = true;
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+
+Cleanup:
+ ret = -1;
+
+ if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
+ if (gHvContext.synICEventPage[0])
+ osd_PageFree(gHvContext.synICEventPage[0], 1);
+
+ if (gHvContext.synICMessagePage[0])
+ osd_PageFree(gHvContext.synICMessagePage[0], 1);
+ }
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * HvSynicCleanup - Cleanup routine for HvSynicInit().
+ */
+void HvSynicCleanup(void)
+{
+ union hv_synic_sint sharedSint;
+ union hv_synic_simp simp;
+ union hv_synic_siefp siefp;
+
+ DPRINT_ENTER(VMBUS);
+
+ if (!gHvContext.SynICInitialized) {
+ DPRINT_EXIT(VMBUS);
+ return;
+ }
+
+ rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
+
+ sharedSint.Masked = 1;
+
+ /* Disable the interrupt */
+ wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
+
+ /*
+ * Disable and free the resources only if we are running as
+ * native linux since in xenlinux, we are sharing the
+ * resources with the x2v shim
+ */
+ if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
+ rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
+ simp.SimpEnabled = 0;
+ simp.BaseSimpGpa = 0;
+
+ wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
+
+ rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
+ siefp.SiefpEnabled = 0;
+ siefp.BaseSiefpGpa = 0;
+
+ wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
+
+ osd_PageFree(gHvContext.synICMessagePage[0], 1);
+ osd_PageFree(gHvContext.synICEventPage[0], 1);
+ }
+
+ DPRINT_EXIT(VMBUS);
+}
diff --git a/drivers/staging/hv/Hv.h b/drivers/staging/hv/Hv.h
new file mode 100644
index 000000000000..5379e4bfc56e
--- /dev/null
+++ b/drivers/staging/hv/Hv.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef __HV_H__
+#define __HV_H__
+
+#include "hv_api.h"
+
+enum {
+ VMBUS_MESSAGE_CONNECTION_ID = 1,
+ VMBUS_MESSAGE_PORT_ID = 1,
+ VMBUS_EVENT_CONNECTION_ID = 2,
+ VMBUS_EVENT_PORT_ID = 2,
+ VMBUS_MONITOR_CONNECTION_ID = 3,
+ VMBUS_MONITOR_PORT_ID = 3,
+ VMBUS_MESSAGE_SINT = 2,
+};
+
+/* #defines */
+
+#define HV_PRESENT_BIT 0x80000000
+
+#define HV_XENLINUX_GUEST_ID_LO 0x00000000
+#define HV_XENLINUX_GUEST_ID_HI 0x0B00B135
+#define HV_XENLINUX_GUEST_ID (((u64)HV_XENLINUX_GUEST_ID_HI << 32) \
+ | HV_XENLINUX_GUEST_ID_LO)
+
+#define HV_LINUX_GUEST_ID_LO 0x00000000
+#define HV_LINUX_GUEST_ID_HI 0xB16B00B5
+#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \
+ HV_LINUX_GUEST_ID_LO)
+
+#define HV_CPU_POWER_MANAGEMENT (1 << 0)
+#define HV_RECOMMENDATIONS_MAX 4
+
+#define HV_X64_MAX 5
+#define HV_CAPS_MAX 8
+
+
+#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64)
+
+
+/* Service definitions */
+
+#define HV_SERVICE_PARENT_PORT (0)
+#define HV_SERVICE_PARENT_CONNECTION (0)
+
+#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0)
+#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1)
+#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2)
+#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3)
+
+#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1)
+#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2)
+#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3)
+#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4)
+#define HV_SERVICE_MAX_MESSAGE_ID (4)
+
+#define HV_SERVICE_PROTOCOL_VERSION (0x0010)
+#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64
+
+/* #define VMBUS_REVISION_NUMBER 6 */
+
+/* Our local vmbus's port and connection id. Anything >0 is fine */
+/* #define VMBUS_PORT_ID 11 */
+
+/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */
+static const struct hv_guid VMBUS_SERVICE_ID = {
+ .data = {
+ 0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c,
+ 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4
+ },
+};
+
+#define MAX_NUM_CPUS 1
+
+
+struct hv_input_signal_event_buffer {
+ u64 Align8;
+ struct hv_input_signal_event Event;
+};
+
+struct hv_context {
+ /* XenLinux or native Linux. If XenLinux, the hypercall and synic pages
+ * has already been initialized */
+ u64 GuestId;
+
+ void *HypercallPage;
+
+ bool SynICInitialized;
+
+ /*
+ * This is used as an input param to HvCallSignalEvent hypercall. The
+ * input param is immutable in our usage and must be dynamic mem (vs
+ * stack or global). */
+ struct hv_input_signal_event_buffer *SignalEventBuffer;
+ /* 8-bytes aligned of the buffer above */
+ struct hv_input_signal_event *SignalEventParam;
+
+ void *synICMessagePage[MAX_NUM_CPUS];
+ void *synICEventPage[MAX_NUM_CPUS];
+};
+
+extern struct hv_context gHvContext;
+
+
+/* Hv Interface */
+
+extern int HvInit(void);
+
+extern void HvCleanup(void);
+
+extern u16 HvPostMessage(union hv_connection_id connectionId,
+ enum hv_message_type messageType,
+ void *payload, size_t payloadSize);
+
+extern u16 HvSignalEvent(void);
+
+extern int HvSynicInit(u32 irqVector);
+
+extern void HvSynicCleanup(void);
+
+#endif /* __HV_H__ */
diff --git a/drivers/staging/hv/Kconfig b/drivers/staging/hv/Kconfig
new file mode 100644
index 000000000000..40447020a790
--- /dev/null
+++ b/drivers/staging/hv/Kconfig
@@ -0,0 +1,32 @@
+config HYPERV
+ tristate "Microsoft Hyper-V client drivers"
+ depends on X86 && m
+ default n
+ help
+ Select this option to run Linux as a Hyper-V client operating
+ system.
+
+if HYPERV
+
+config HYPERV_STORAGE
+ tristate "Microsoft Hyper-V virtual storage driver"
+ depends on SCSI
+ default HYPERV
+ help
+ Select this option to enable the Hyper-V virtual storage driver.
+
+config HYPERV_BLOCK
+ tristate "Microsoft Hyper-V virtual block driver"
+ depends on BLOCK && SCSI
+ default HYPERV
+ help
+ Select this option to enable the Hyper-V virtual block driver.
+
+config HYPERV_NET
+ tristate "Microsoft Hyper-V virtual network driver"
+ depends on NET
+ default HYPERV
+ help
+ Select this option to enable the Hyper-V virtual network driver.
+
+endif
diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile
new file mode 100644
index 000000000000..27ebae8a9185
--- /dev/null
+++ b/drivers/staging/hv/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_HYPERV) += hv_vmbus.o
+obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
+obj-$(CONFIG_HYPERV_BLOCK) += hv_blkvsc.o
+obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o
+
+hv_vmbus-objs := vmbus_drv.o osd.o \
+ Vmbus.o Hv.o Connection.o Channel.o \
+ ChannelMgmt.o ChannelInterface.o RingBuffer.o
+hv_storvsc-objs := storvsc_drv.o StorVsc.o
+hv_blkvsc-objs := blkvsc_drv.o BlkVsc.o
+hv_netvsc-objs := netvsc_drv.o NetVsc.o RndisFilter.o
diff --git a/drivers/staging/hv/NetVsc.c b/drivers/staging/hv/NetVsc.c
new file mode 100644
index 000000000000..1610b845198f
--- /dev/null
+++ b/drivers/staging/hv/NetVsc.c
@@ -0,0 +1,1379 @@
+/*
+ * 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:
+ * Hank Janssen <hjanssen@microsoft.com>
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include "osd.h"
+#include "logging.h"
+#include "NetVsc.h"
+#include "RndisFilter.h"
+
+
+/* Globals */
+static const char *gDriverName = "netvsc";
+
+/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
+static const struct hv_guid gNetVscDeviceType = {
+ .data = {
+ 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
+ 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
+ }
+};
+
+static int NetVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo);
+
+static int NetVscOnDeviceRemove(struct hv_device *Device);
+
+static void NetVscOnCleanup(struct hv_driver *Driver);
+
+static void NetVscOnChannelCallback(void *context);
+
+static int NetVscInitializeSendBufferWithNetVsp(struct hv_device *Device);
+
+static int NetVscInitializeReceiveBufferWithNetVsp(struct hv_device *Device);
+
+static int NetVscDestroySendBuffer(struct netvsc_device *NetDevice);
+
+static int NetVscDestroyReceiveBuffer(struct netvsc_device *NetDevice);
+
+static int NetVscConnectToVsp(struct hv_device *Device);
+
+static void NetVscOnSendCompletion(struct hv_device *Device,
+ struct vmpacket_descriptor *Packet);
+
+static int NetVscOnSend(struct hv_device *Device,
+ struct hv_netvsc_packet *Packet);
+
+static void NetVscOnReceive(struct hv_device *Device,
+ struct vmpacket_descriptor *Packet);
+
+static void NetVscOnReceiveCompletion(void *Context);
+
+static void NetVscSendReceiveCompletion(struct hv_device *Device,
+ u64 TransactionId);
+
+
+static struct netvsc_device *AllocNetDevice(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice;
+
+ netDevice = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
+ if (!netDevice)
+ return NULL;
+
+ /* Set to 2 to allow both inbound and outbound traffic */
+ atomic_cmpxchg(&netDevice->RefCount, 0, 2);
+
+ netDevice->Device = Device;
+ Device->Extension = netDevice;
+
+ return netDevice;
+}
+
+static void FreeNetDevice(struct netvsc_device *Device)
+{
+ ASSERT(atomic_read(&Device->RefCount) == 0);
+ Device->Device->Extension = NULL;
+ kfree(Device);
+}
+
+
+/* Get the net device object iff exists and its refcount > 1 */
+static struct netvsc_device *GetOutboundNetDevice(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice;
+
+ netDevice = Device->Extension;
+ if (netDevice && atomic_read(&netDevice->RefCount) > 1)
+ atomic_inc(&netDevice->RefCount);
+ else
+ netDevice = NULL;
+
+ return netDevice;
+}
+
+/* Get the net device object iff exists and its refcount > 0 */
+static struct netvsc_device *GetInboundNetDevice(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice;
+
+ netDevice = Device->Extension;
+ if (netDevice && atomic_read(&netDevice->RefCount))
+ atomic_inc(&netDevice->RefCount);
+ else
+ netDevice = NULL;
+
+ return netDevice;
+}
+
+static void PutNetDevice(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice;
+
+ netDevice = Device->Extension;
+ ASSERT(netDevice);
+
+ atomic_dec(&netDevice->RefCount);
+}
+
+static struct netvsc_device *ReleaseOutboundNetDevice(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice;
+
+ netDevice = Device->Extension;
+ if (netDevice == NULL)
+ return NULL;
+
+ /* Busy wait until the ref drop to 2, then set it to 1 */
+ while (atomic_cmpxchg(&netDevice->RefCount, 2, 1) != 2)
+ udelay(100);
+
+ return netDevice;
+}
+
+static struct netvsc_device *ReleaseInboundNetDevice(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice;
+
+ netDevice = Device->Extension;
+ if (netDevice == NULL)
+ return NULL;
+
+ /* Busy wait until the ref drop to 1, then set it to 0 */
+ while (atomic_cmpxchg(&netDevice->RefCount, 1, 0) != 1)
+ udelay(100);
+
+ Device->Extension = NULL;
+ return netDevice;
+}
+
+/**
+ * NetVscInitialize - Main entry point
+ */
+int NetVscInitialize(struct hv_driver *drv)
+{
+ struct netvsc_driver *driver = (struct netvsc_driver *)drv;
+
+ DPRINT_ENTER(NETVSC);
+
+ DPRINT_DBG(NETVSC, "sizeof(struct hv_netvsc_packet)=%zd, "
+ "sizeof(struct nvsp_message)=%zd, "
+ "sizeof(struct vmtransfer_page_packet_header)=%zd",
+ sizeof(struct hv_netvsc_packet),
+ sizeof(struct nvsp_message),
+ sizeof(struct vmtransfer_page_packet_header));
+
+ /* Make sure we are at least 2 pages since 1 page is used for control */
+ ASSERT(driver->RingBufferSize >= (PAGE_SIZE << 1));
+
+ drv->name = gDriverName;
+ memcpy(&drv->deviceType, &gNetVscDeviceType, sizeof(struct hv_guid));
+
+ /* Make sure it is set by the caller */
+ ASSERT(driver->OnReceiveCallback);
+ ASSERT(driver->OnLinkStatusChanged);
+
+ /* Setup the dispatch table */
+ driver->Base.OnDeviceAdd = NetVscOnDeviceAdd;
+ driver->Base.OnDeviceRemove = NetVscOnDeviceRemove;
+ driver->Base.OnCleanup = NetVscOnCleanup;
+
+ driver->OnSend = NetVscOnSend;
+
+ RndisFilterInit(driver);
+
+ DPRINT_EXIT(NETVSC);
+
+ return 0;
+}
+
+static int NetVscInitializeReceiveBufferWithNetVsp(struct hv_device *Device)
+{
+ int ret = 0;
+ struct netvsc_device *netDevice;
+ struct nvsp_message *initPacket;
+
+ DPRINT_ENTER(NETVSC);
+
+ netDevice = GetOutboundNetDevice(Device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "unable to get net device..."
+ "device being destroyed?");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+ ASSERT(netDevice->ReceiveBufferSize > 0);
+ /* page-size grandularity */
+ ASSERT((netDevice->ReceiveBufferSize & (PAGE_SIZE - 1)) == 0);
+
+ netDevice->ReceiveBuffer =
+ osd_PageAlloc(netDevice->ReceiveBufferSize >> PAGE_SHIFT);
+ if (!netDevice->ReceiveBuffer) {
+ DPRINT_ERR(NETVSC,
+ "unable to allocate receive buffer of size %d",
+ netDevice->ReceiveBufferSize);
+ ret = -1;
+ goto Cleanup;
+ }
+ /* page-aligned buffer */
+ ASSERT(((unsigned long)netDevice->ReceiveBuffer & (PAGE_SIZE - 1)) ==
+ 0);
+
+ DPRINT_INFO(NETVSC, "Establishing receive buffer's GPADL...");
+
+ /*
+ * 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 = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device,
+ netDevice->ReceiveBuffer,
+ netDevice->ReceiveBufferSize,
+ &netDevice->ReceiveBufferGpadlHandle);
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC,
+ "unable to establish receive buffer's gpadl");
+ goto Cleanup;
+ }
+
+ /* osd_WaitEventWait(ext->ChannelInitEvent); */
+
+ /* Notify the NetVsp of the gpadl handle */
+ DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendReceiveBuffer...");
+
+ initPacket = &netDevice->ChannelInitPacket;
+
+ memset(initPacket, 0, sizeof(struct nvsp_message));
+
+ initPacket->Header.MessageType = NvspMessage1TypeSendReceiveBuffer;
+ initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->ReceiveBufferGpadlHandle;
+ initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID;
+
+ /* Send the gpadl notification request */
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ initPacket,
+ sizeof(struct nvsp_message),
+ (unsigned long)initPacket,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC,
+ "unable to send receive buffer's gpadl to netvsp");
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(netDevice->ChannelInitEvent);
+
+ /* Check the response */
+ if (initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status != NvspStatusSuccess) {
+ DPRINT_ERR(NETVSC, "Unable to complete receive buffer "
+ "initialzation with NetVsp - status %d",
+ initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status);
+ ret = -1;
+ goto Cleanup;
+ }
+
+ /* Parse the response */
+ ASSERT(netDevice->ReceiveSectionCount == 0);
+ ASSERT(netDevice->ReceiveSections == NULL);
+
+ netDevice->ReceiveSectionCount = initPacket->Messages.Version1Messages.SendReceiveBufferComplete.NumSections;
+
+ netDevice->ReceiveSections = kmalloc(netDevice->ReceiveSectionCount * sizeof(struct nvsp_1_receive_buffer_section), GFP_KERNEL);
+ if (netDevice->ReceiveSections == NULL) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ memcpy(netDevice->ReceiveSections,
+ initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Sections,
+ netDevice->ReceiveSectionCount * sizeof(struct nvsp_1_receive_buffer_section));
+
+ DPRINT_INFO(NETVSC, "Receive sections info (count %d, offset %d, "
+ "endoffset %d, suballoc size %d, num suballocs %d)",
+ netDevice->ReceiveSectionCount,
+ netDevice->ReceiveSections[0].Offset,
+ netDevice->ReceiveSections[0].EndOffset,
+ netDevice->ReceiveSections[0].SubAllocationSize,
+ netDevice->ReceiveSections[0].NumSubAllocations);
+
+ /*
+ * For 1st release, there should only be 1 section that represents the
+ * entire receive buffer
+ */
+ if (netDevice->ReceiveSectionCount != 1 ||
+ netDevice->ReceiveSections->Offset != 0) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ goto Exit;
+
+Cleanup:
+ NetVscDestroyReceiveBuffer(netDevice);
+
+Exit:
+ PutNetDevice(Device);
+ DPRINT_EXIT(NETVSC);
+ return ret;
+}
+
+static int NetVscInitializeSendBufferWithNetVsp(struct hv_device *Device)
+{
+ int ret = 0;
+ struct netvsc_device *netDevice;
+ struct nvsp_message *initPacket;
+
+ DPRINT_ENTER(NETVSC);
+
+ netDevice = GetOutboundNetDevice(Device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "unable to get net device..."
+ "device being destroyed?");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+ ASSERT(netDevice->SendBufferSize > 0);
+ /* page-size grandularity */
+ ASSERT((netDevice->SendBufferSize & (PAGE_SIZE - 1)) == 0);
+
+ netDevice->SendBuffer =
+ osd_PageAlloc(netDevice->SendBufferSize >> PAGE_SHIFT);
+ if (!netDevice->SendBuffer) {
+ DPRINT_ERR(NETVSC, "unable to allocate send buffer of size %d",
+ netDevice->SendBufferSize);
+ ret = -1;
+ goto Cleanup;
+ }
+ /* page-aligned buffer */
+ ASSERT(((unsigned long)netDevice->SendBuffer & (PAGE_SIZE - 1)) == 0);
+
+ DPRINT_INFO(NETVSC, "Establishing send buffer's GPADL...");
+
+ /*
+ * 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 = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device,
+ netDevice->SendBuffer,
+ netDevice->SendBufferSize,
+ &netDevice->SendBufferGpadlHandle);
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC, "unable to establish send buffer's gpadl");
+ goto Cleanup;
+ }
+
+ /* osd_WaitEventWait(ext->ChannelInitEvent); */
+
+ /* Notify the NetVsp of the gpadl handle */
+ DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendSendBuffer...");
+
+ initPacket = &netDevice->ChannelInitPacket;
+
+ memset(initPacket, 0, sizeof(struct nvsp_message));
+
+ initPacket->Header.MessageType = NvspMessage1TypeSendSendBuffer;
+ initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->SendBufferGpadlHandle;
+ initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_SEND_BUFFER_ID;
+
+ /* Send the gpadl notification request */
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ initPacket, sizeof(struct nvsp_message),
+ (unsigned long)initPacket,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC,
+ "unable to send receive buffer's gpadl to netvsp");
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(netDevice->ChannelInitEvent);
+
+ /* Check the response */
+ if (initPacket->Messages.Version1Messages.SendSendBufferComplete.Status != NvspStatusSuccess) {
+ DPRINT_ERR(NETVSC, "Unable to complete send buffer "
+ "initialzation with NetVsp - status %d",
+ initPacket->Messages.Version1Messages.SendSendBufferComplete.Status);
+ ret = -1;
+ goto Cleanup;
+ }
+
+ netDevice->SendSectionSize = initPacket->Messages.Version1Messages.SendSendBufferComplete.SectionSize;
+
+ goto Exit;
+
+Cleanup:
+ NetVscDestroySendBuffer(netDevice);
+
+Exit:
+ PutNetDevice(Device);
+ DPRINT_EXIT(NETVSC);
+ return ret;
+}
+
+static int NetVscDestroyReceiveBuffer(struct netvsc_device *NetDevice)
+{
+ struct nvsp_message *revokePacket;
+ int ret = 0;
+
+ DPRINT_ENTER(NETVSC);
+
+ /*
+ * 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 (NetDevice->ReceiveSectionCount) {
+ DPRINT_INFO(NETVSC,
+ "Sending NvspMessage1TypeRevokeReceiveBuffer...");
+
+ /* Send the revoke receive buffer */
+ revokePacket = &NetDevice->RevokePacket;
+ memset(revokePacket, 0, sizeof(struct nvsp_message));
+
+ revokePacket->Header.MessageType = NvspMessage1TypeRevokeReceiveBuffer;
+ revokePacket->Messages.Version1Messages.RevokeReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID;
+
+ ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket(
+ NetDevice->Device,
+ revokePacket,
+ sizeof(struct nvsp_message),
+ (unsigned long)revokePacket,
+ VmbusPacketTypeDataInBand, 0);
+ /*
+ * If we failed here, we might as well return and
+ * have a leak rather than continue and a bugchk
+ */
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC, "unable to send revoke receive "
+ "buffer to netvsp");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+ }
+
+ /* Teardown the gpadl on the vsp end */
+ if (NetDevice->ReceiveBufferGpadlHandle) {
+ DPRINT_INFO(NETVSC, "Tearing down receive buffer's GPADL...");
+
+ ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl(
+ NetDevice->Device,
+ NetDevice->ReceiveBufferGpadlHandle);
+
+ /* If we failed here, we might as well return and have a leak rather than continue and a bugchk */
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC,
+ "unable to teardown receive buffer's gpadl");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+ NetDevice->ReceiveBufferGpadlHandle = 0;
+ }
+
+ if (NetDevice->ReceiveBuffer) {
+ DPRINT_INFO(NETVSC, "Freeing up receive buffer...");
+
+ /* Free up the receive buffer */
+ osd_PageFree(NetDevice->ReceiveBuffer,
+ NetDevice->ReceiveBufferSize >> PAGE_SHIFT);
+ NetDevice->ReceiveBuffer = NULL;
+ }
+
+ if (NetDevice->ReceiveSections) {
+ NetDevice->ReceiveSectionCount = 0;
+ kfree(NetDevice->ReceiveSections);
+ NetDevice->ReceiveSections = NULL;
+ }
+
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static int NetVscDestroySendBuffer(struct netvsc_device *NetDevice)
+{
+ struct nvsp_message *revokePacket;
+ int ret = 0;
+
+ DPRINT_ENTER(NETVSC);
+
+ /*
+ * 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 (NetDevice->SendSectionSize) {
+ DPRINT_INFO(NETVSC,
+ "Sending NvspMessage1TypeRevokeSendBuffer...");
+
+ /* Send the revoke send buffer */
+ revokePacket = &NetDevice->RevokePacket;
+ memset(revokePacket, 0, sizeof(struct nvsp_message));
+
+ revokePacket->Header.MessageType = NvspMessage1TypeRevokeSendBuffer;
+ revokePacket->Messages.Version1Messages.RevokeSendBuffer.Id = NETVSC_SEND_BUFFER_ID;
+
+ ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket(NetDevice->Device,
+ revokePacket,
+ sizeof(struct nvsp_message),
+ (unsigned long)revokePacket,
+ VmbusPacketTypeDataInBand, 0);
+ /*
+ * If we failed here, we might as well return and have a leak
+ * rather than continue and a bugchk
+ */
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC, "unable to send revoke send buffer "
+ "to netvsp");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+ }
+
+ /* Teardown the gpadl on the vsp end */
+ if (NetDevice->SendBufferGpadlHandle) {
+ DPRINT_INFO(NETVSC, "Tearing down send buffer's GPADL...");
+
+ ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl(NetDevice->Device, NetDevice->SendBufferGpadlHandle);
+
+ /*
+ * If we failed here, we might as well return and have a leak
+ * rather than continue and a bugchk
+ */
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC, "unable to teardown send buffer's "
+ "gpadl");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+ NetDevice->SendBufferGpadlHandle = 0;
+ }
+
+ if (NetDevice->SendBuffer) {
+ DPRINT_INFO(NETVSC, "Freeing up send buffer...");
+
+ /* Free up the receive buffer */
+ osd_PageFree(NetDevice->SendBuffer,
+ NetDevice->SendBufferSize >> PAGE_SHIFT);
+ NetDevice->SendBuffer = NULL;
+ }
+
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+
+static int NetVscConnectToVsp(struct hv_device *Device)
+{
+ int ret;
+ struct netvsc_device *netDevice;
+ struct nvsp_message *initPacket;
+ int ndisVersion;
+
+ DPRINT_ENTER(NETVSC);
+
+ netDevice = GetOutboundNetDevice(Device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "unable to get net device..."
+ "device being destroyed?");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+
+ initPacket = &netDevice->ChannelInitPacket;
+
+ memset(initPacket, 0, sizeof(struct nvsp_message));
+ initPacket->Header.MessageType = NvspMessageTypeInit;
+ initPacket->Messages.InitMessages.Init.MinProtocolVersion = NVSP_MIN_PROTOCOL_VERSION;
+ initPacket->Messages.InitMessages.Init.MaxProtocolVersion = NVSP_MAX_PROTOCOL_VERSION;
+
+ DPRINT_INFO(NETVSC, "Sending NvspMessageTypeInit...");
+
+ /* Send the init request */
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ initPacket,
+ sizeof(struct nvsp_message),
+ (unsigned long)initPacket,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC, "unable to send NvspMessageTypeInit");
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(netDevice->ChannelInitEvent);
+
+ /* Now, check the response */
+ /* ASSERT(initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength <= MAX_MULTIPAGE_BUFFER_COUNT); */
+ DPRINT_INFO(NETVSC, "NvspMessageTypeInit status(%d) max mdl chain (%d)",
+ initPacket->Messages.InitMessages.InitComplete.Status,
+ initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength);
+
+ if (initPacket->Messages.InitMessages.InitComplete.Status !=
+ NvspStatusSuccess) {
+ DPRINT_ERR(NETVSC,
+ "unable to initialize with netvsp (status 0x%x)",
+ initPacket->Messages.InitMessages.InitComplete.Status);
+ ret = -1;
+ goto Cleanup;
+ }
+
+ if (initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion != NVSP_PROTOCOL_VERSION_1) {
+ DPRINT_ERR(NETVSC, "unable to initialize with netvsp "
+ "(version expected 1 got %d)",
+ initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion);
+ ret = -1;
+ goto Cleanup;
+ }
+ DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendNdisVersion...");
+
+ /* Send the ndis version */
+ memset(initPacket, 0, sizeof(struct nvsp_message));
+
+ ndisVersion = 0x00050000;
+
+ initPacket->Header.MessageType = NvspMessage1TypeSendNdisVersion;
+ initPacket->Messages.Version1Messages.SendNdisVersion.NdisMajorVersion =
+ (ndisVersion & 0xFFFF0000) >> 16;
+ initPacket->Messages.Version1Messages.SendNdisVersion.NdisMinorVersion =
+ ndisVersion & 0xFFFF;
+
+ /* Send the init request */
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ initPacket,
+ sizeof(struct nvsp_message),
+ (unsigned long)initPacket,
+ VmbusPacketTypeDataInBand, 0);
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC,
+ "unable to send NvspMessage1TypeSendNdisVersion");
+ ret = -1;
+ goto Cleanup;
+ }
+ /*
+ * BUGBUG - We have to wait for the above msg since the
+ * netvsp uses KMCL which acknowledges packet (completion
+ * packet) since our Vmbus always set the
+ * VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED flag
+ */
+ /* osd_WaitEventWait(NetVscChannel->ChannelInitEvent); */
+
+ /* Post the big receive buffer to NetVSP */
+ ret = NetVscInitializeReceiveBufferWithNetVsp(Device);
+ if (ret == 0)
+ ret = NetVscInitializeSendBufferWithNetVsp(Device);
+
+Cleanup:
+ PutNetDevice(Device);
+ DPRINT_EXIT(NETVSC);
+ return ret;
+}
+
+static void NetVscDisconnectFromVsp(struct netvsc_device *NetDevice)
+{
+ DPRINT_ENTER(NETVSC);
+
+ NetVscDestroyReceiveBuffer(NetDevice);
+ NetVscDestroySendBuffer(NetDevice);
+
+ DPRINT_EXIT(NETVSC);
+}
+
+/**
+ * NetVscOnDeviceAdd - Callback when the device belonging to this driver is added
+ */
+static int NetVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo)
+{
+ int ret = 0;
+ int i;
+ struct netvsc_device *netDevice;
+ struct hv_netvsc_packet *packet, *pos;
+ struct netvsc_driver *netDriver =
+ (struct netvsc_driver *)Device->Driver;
+
+ DPRINT_ENTER(NETVSC);
+
+ netDevice = AllocNetDevice(Device);
+ if (!netDevice) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ DPRINT_DBG(NETVSC, "netvsc channel object allocated - %p", netDevice);
+
+ /* Initialize the NetVSC channel extension */
+ netDevice->ReceiveBufferSize = NETVSC_RECEIVE_BUFFER_SIZE;
+ spin_lock_init(&netDevice->receive_packet_list_lock);
+
+ netDevice->SendBufferSize = NETVSC_SEND_BUFFER_SIZE;
+
+ INIT_LIST_HEAD(&netDevice->ReceivePacketList);
+
+ 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) {
+ DPRINT_DBG(NETVSC, "unable to allocate netvsc pkts "
+ "for receive pool (wanted %d got %d)",
+ NETVSC_RECEIVE_PACKETLIST_COUNT, i);
+ break;
+ }
+ list_add_tail(&packet->ListEntry,
+ &netDevice->ReceivePacketList);
+ }
+ netDevice->ChannelInitEvent = osd_WaitEventCreate();
+
+ /* Open the channel */
+ ret = Device->Driver->VmbusChannelInterface.Open(Device,
+ netDriver->RingBufferSize,
+ netDriver->RingBufferSize,
+ NULL, 0,
+ NetVscOnChannelCallback,
+ Device);
+
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC, "unable to open channel: %d", ret);
+ ret = -1;
+ goto Cleanup;
+ }
+
+ /* Channel is opened */
+ DPRINT_INFO(NETVSC, "*** NetVSC channel opened successfully! ***");
+
+ /* Connect with the NetVsp */
+ ret = NetVscConnectToVsp(Device);
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC, "unable to connect to NetVSP - %d", ret);
+ ret = -1;
+ goto Close;
+ }
+
+ DPRINT_INFO(NETVSC, "*** NetVSC channel handshake result - %d ***",
+ ret);
+
+ DPRINT_EXIT(NETVSC);
+ return ret;
+
+Close:
+ /* Now, we can close the channel safely */
+ Device->Driver->VmbusChannelInterface.Close(Device);
+
+Cleanup:
+
+ if (netDevice) {
+ kfree(netDevice->ChannelInitEvent);
+
+ list_for_each_entry_safe(packet, pos,
+ &netDevice->ReceivePacketList,
+ ListEntry) {
+ list_del(&packet->ListEntry);
+ kfree(packet);
+ }
+
+ ReleaseOutboundNetDevice(Device);
+ ReleaseInboundNetDevice(Device);
+
+ FreeNetDevice(netDevice);
+ }
+
+ DPRINT_EXIT(NETVSC);
+ return ret;
+}
+
+/**
+ * NetVscOnDeviceRemove - Callback when the root bus device is removed
+ */
+static int NetVscOnDeviceRemove(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice;
+ struct hv_netvsc_packet *netvscPacket, *pos;
+
+ DPRINT_ENTER(NETVSC);
+
+ DPRINT_INFO(NETVSC, "Disabling outbound traffic on net device (%p)...",
+ Device->Extension);
+
+ /* Stop outbound traffic ie sends and receives completions */
+ netDevice = ReleaseOutboundNetDevice(Device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "No net device present!!");
+ return -1;
+ }
+
+ /* Wait for all send completions */
+ while (atomic_read(&netDevice->NumOutstandingSends)) {
+ DPRINT_INFO(NETVSC, "waiting for %d requests to complete...",
+ atomic_read(&netDevice->NumOutstandingSends));
+ udelay(100);
+ }
+
+ DPRINT_INFO(NETVSC, "Disconnecting from netvsp...");
+
+ NetVscDisconnectFromVsp(netDevice);
+
+ DPRINT_INFO(NETVSC, "Disabling inbound traffic on net device (%p)...",
+ Device->Extension);
+
+ /* Stop inbound traffic ie receives and sends completions */
+ netDevice = ReleaseInboundNetDevice(Device);
+
+ /* At this point, no one should be accessing netDevice except in here */
+ DPRINT_INFO(NETVSC, "net device (%p) safe to remove", netDevice);
+
+ /* Now, we can close the channel safely */
+ Device->Driver->VmbusChannelInterface.Close(Device);
+
+ /* Release all resources */
+ list_for_each_entry_safe(netvscPacket, pos,
+ &netDevice->ReceivePacketList, ListEntry) {
+ list_del(&netvscPacket->ListEntry);
+ kfree(netvscPacket);
+ }
+
+ kfree(netDevice->ChannelInitEvent);
+ FreeNetDevice(netDevice);
+
+ DPRINT_EXIT(NETVSC);
+ return 0;
+}
+
+/**
+ * NetVscOnCleanup - Perform any cleanup when the driver is removed
+ */
+static void NetVscOnCleanup(struct hv_driver *drv)
+{
+ DPRINT_ENTER(NETVSC);
+ DPRINT_EXIT(NETVSC);
+}
+
+static void NetVscOnSendCompletion(struct hv_device *Device,
+ struct vmpacket_descriptor *Packet)
+{
+ struct netvsc_device *netDevice;
+ struct nvsp_message *nvspPacket;
+ struct hv_netvsc_packet *nvscPacket;
+
+ DPRINT_ENTER(NETVSC);
+
+ netDevice = GetInboundNetDevice(Device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "unable to get net device..."
+ "device being destroyed?");
+ DPRINT_EXIT(NETVSC);
+ return;
+ }
+
+ nvspPacket = (struct nvsp_message *)((unsigned long)Packet + (Packet->DataOffset8 << 3));
+
+ DPRINT_DBG(NETVSC, "send completion packet - type %d",
+ nvspPacket->Header.MessageType);
+
+ if ((nvspPacket->Header.MessageType == NvspMessageTypeInitComplete) ||
+ (nvspPacket->Header.MessageType ==
+ NvspMessage1TypeSendReceiveBufferComplete) ||
+ (nvspPacket->Header.MessageType ==
+ NvspMessage1TypeSendSendBufferComplete)) {
+ /* Copy the response back */
+ memcpy(&netDevice->ChannelInitPacket, nvspPacket,
+ sizeof(struct nvsp_message));
+ osd_WaitEventSet(netDevice->ChannelInitEvent);
+ } else if (nvspPacket->Header.MessageType ==
+ NvspMessage1TypeSendRNDISPacketComplete) {
+ /* Get the send context */
+ nvscPacket = (struct hv_netvsc_packet *)(unsigned long)Packet->TransactionId;
+ ASSERT(nvscPacket);
+
+ /* Notify the layer above us */
+ nvscPacket->Completion.Send.OnSendCompletion(nvscPacket->Completion.Send.SendCompletionContext);
+
+ atomic_dec(&netDevice->NumOutstandingSends);
+ } else {
+ DPRINT_ERR(NETVSC, "Unknown send completion packet type - "
+ "%d received!!", nvspPacket->Header.MessageType);
+ }
+
+ PutNetDevice(Device);
+ DPRINT_EXIT(NETVSC);
+}
+
+static int NetVscOnSend(struct hv_device *Device,
+ struct hv_netvsc_packet *Packet)
+{
+ struct netvsc_device *netDevice;
+ int ret = 0;
+
+ struct nvsp_message sendMessage;
+
+ DPRINT_ENTER(NETVSC);
+
+ netDevice = GetOutboundNetDevice(Device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "net device (%p) shutting down..."
+ "ignoring outbound packets", netDevice);
+ DPRINT_EXIT(NETVSC);
+ return -2;
+ }
+
+ sendMessage.Header.MessageType = NvspMessage1TypeSendRNDISPacket;
+ if (Packet->IsDataPacket) {
+ /* 0 is RMC_DATA; */
+ sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 0;
+ } else {
+ /* 1 is RMC_CONTROL; */
+ sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 1;
+ }
+
+ /* Not using send buffer section */
+ sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionIndex = 0xFFFFFFFF;
+ sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionSize = 0;
+
+ if (Packet->PageBufferCount) {
+ ret = Device->Driver->VmbusChannelInterface.SendPacketPageBuffer(
+ Device, Packet->PageBuffers,
+ Packet->PageBufferCount,
+ &sendMessage,
+ sizeof(struct nvsp_message),
+ (unsigned long)Packet);
+ } else {
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ &sendMessage,
+ sizeof(struct nvsp_message),
+ (unsigned long)Packet,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+ }
+
+ if (ret != 0)
+ DPRINT_ERR(NETVSC, "Unable to send packet %p ret %d",
+ Packet, ret);
+
+ atomic_inc(&netDevice->NumOutstandingSends);
+ PutNetDevice(Device);
+
+ DPRINT_EXIT(NETVSC);
+ return ret;
+}
+
+static void NetVscOnReceive(struct hv_device *Device,
+ struct vmpacket_descriptor *Packet)
+{
+ struct netvsc_device *netDevice;
+ struct vmtransfer_page_packet_header *vmxferpagePacket;
+ struct nvsp_message *nvspPacket;
+ struct hv_netvsc_packet *netvscPacket = NULL;
+ unsigned long start;
+ unsigned long end, endVirtual;
+ /* struct netvsc_driver *netvscDriver; */
+ struct xferpage_packet *xferpagePacket = NULL;
+ int i, j;
+ int count = 0, bytesRemain = 0;
+ unsigned long flags;
+ LIST_HEAD(listHead);
+
+ DPRINT_ENTER(NETVSC);
+
+ netDevice = GetInboundNetDevice(Device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "unable to get net device..."
+ "device being destroyed?");
+ DPRINT_EXIT(NETVSC);
+ return;
+ }
+
+ /*
+ * All inbound packets other than send completion should be xfer page
+ * packet
+ */
+ if (Packet->Type != VmbusPacketTypeDataUsingTransferPages) {
+ DPRINT_ERR(NETVSC, "Unknown packet type received - %d",
+ Packet->Type);
+ PutNetDevice(Device);
+ return;
+ }
+
+ nvspPacket = (struct nvsp_message *)((unsigned long)Packet +
+ (Packet->DataOffset8 << 3));
+
+ /* Make sure this is a valid nvsp packet */
+ if (nvspPacket->Header.MessageType != NvspMessage1TypeSendRNDISPacket) {
+ DPRINT_ERR(NETVSC, "Unknown nvsp packet type received - %d",
+ nvspPacket->Header.MessageType);
+ PutNetDevice(Device);
+ return;
+ }
+
+ DPRINT_DBG(NETVSC, "NVSP packet received - type %d",
+ nvspPacket->Header.MessageType);
+
+ vmxferpagePacket = (struct vmtransfer_page_packet_header *)Packet;
+
+ if (vmxferpagePacket->TransferPageSetId != NETVSC_RECEIVE_BUFFER_ID) {
+ DPRINT_ERR(NETVSC, "Invalid xfer page set id - "
+ "expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID,
+ vmxferpagePacket->TransferPageSetId);
+ PutNetDevice(Device);
+ return;
+ }
+
+ DPRINT_DBG(NETVSC, "xfer page - range count %d",
+ vmxferpagePacket->RangeCount);
+
+ /*
+ * 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(&netDevice->receive_packet_list_lock, flags);
+ while (!list_empty(&netDevice->ReceivePacketList)) {
+ list_move_tail(&netDevice->ReceivePacketList, &listHead);
+ if (++count == vmxferpagePacket->RangeCount + 1)
+ break;
+ }
+ spin_unlock_irqrestore(&netDevice->receive_packet_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) {
+ DPRINT_ERR(NETVSC, "Got only %d netvsc pkt...needed %d pkts. "
+ "Dropping this xfer page packet completely!",
+ count, vmxferpagePacket->RangeCount + 1);
+
+ /* Return it to the freelist */
+ spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags);
+ for (i = count; i != 0; i--) {
+ list_move_tail(&listHead,
+ &netDevice->ReceivePacketList);
+ }
+ spin_unlock_irqrestore(&netDevice->receive_packet_list_lock,
+ flags);
+
+ NetVscSendReceiveCompletion(Device,
+ vmxferpagePacket->d.TransactionId);
+
+ PutNetDevice(Device);
+ return;
+ }
+
+ /* Remove the 1st packet to represent the xfer page packet itself */
+ xferpagePacket = list_entry(&listHead, struct xferpage_packet,
+ ListEntry);
+ list_del(&xferpagePacket->ListEntry);
+
+ /* This is how much we can satisfy */
+ xferpagePacket->Count = count - 1;
+ ASSERT(xferpagePacket->Count > 0 && xferpagePacket->Count <=
+ vmxferpagePacket->RangeCount);
+
+ if (xferpagePacket->Count != vmxferpagePacket->RangeCount) {
+ DPRINT_INFO(NETVSC, "Needed %d netvsc pkts to satisy this xfer "
+ "page...got %d", vmxferpagePacket->RangeCount,
+ xferpagePacket->Count);
+ }
+
+ /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
+ for (i = 0; i < (count - 1); i++) {
+ netvscPacket = list_entry(&listHead, struct hv_netvsc_packet,
+ ListEntry);
+ list_del(&netvscPacket->ListEntry);
+
+ /* Initialize the netvsc packet */
+ netvscPacket->XferPagePacket = xferpagePacket;
+ netvscPacket->Completion.Recv.OnReceiveCompletion =
+ NetVscOnReceiveCompletion;
+ netvscPacket->Completion.Recv.ReceiveCompletionContext =
+ netvscPacket;
+ netvscPacket->Device = Device;
+ /* Save this so that we can send it back */
+ netvscPacket->Completion.Recv.ReceiveCompletionTid =
+ vmxferpagePacket->d.TransactionId;
+
+ netvscPacket->TotalDataBufferLength =
+ vmxferpagePacket->Ranges[i].ByteCount;
+ netvscPacket->PageBufferCount = 1;
+
+ ASSERT(vmxferpagePacket->Ranges[i].ByteOffset +
+ vmxferpagePacket->Ranges[i].ByteCount <
+ netDevice->ReceiveBufferSize);
+
+ netvscPacket->PageBuffers[0].Length =
+ vmxferpagePacket->Ranges[i].ByteCount;
+
+ start = virt_to_phys((void *)((unsigned long)netDevice->ReceiveBuffer + vmxferpagePacket->Ranges[i].ByteOffset));
+
+ netvscPacket->PageBuffers[0].Pfn = start >> PAGE_SHIFT;
+ endVirtual = (unsigned long)netDevice->ReceiveBuffer
+ + vmxferpagePacket->Ranges[i].ByteOffset
+ + vmxferpagePacket->Ranges[i].ByteCount - 1;
+ end = virt_to_phys((void *)endVirtual);
+
+ /* Calculate the page relative offset */
+ netvscPacket->PageBuffers[0].Offset =
+ vmxferpagePacket->Ranges[i].ByteOffset & (PAGE_SIZE - 1);
+ if ((end >> PAGE_SHIFT) != (start >> PAGE_SHIFT)) {
+ /* Handle frame across multiple pages: */
+ netvscPacket->PageBuffers[0].Length =
+ (netvscPacket->PageBuffers[0].Pfn << PAGE_SHIFT)
+ + PAGE_SIZE - start;
+ bytesRemain = netvscPacket->TotalDataBufferLength -
+ netvscPacket->PageBuffers[0].Length;
+ for (j = 1; j < NETVSC_PACKET_MAXPAGE; j++) {
+ netvscPacket->PageBuffers[j].Offset = 0;
+ if (bytesRemain <= PAGE_SIZE) {
+ netvscPacket->PageBuffers[j].Length = bytesRemain;
+ bytesRemain = 0;
+ } else {
+ netvscPacket->PageBuffers[j].Length = PAGE_SIZE;
+ bytesRemain -= PAGE_SIZE;
+ }
+ netvscPacket->PageBuffers[j].Pfn =
+ virt_to_phys((void *)(endVirtual - bytesRemain)) >> PAGE_SHIFT;
+ netvscPacket->PageBufferCount++;
+ if (bytesRemain == 0)
+ break;
+ }
+ ASSERT(bytesRemain == 0);
+ }
+ DPRINT_DBG(NETVSC, "[%d] - (abs offset %u len %u) => "
+ "(pfn %llx, offset %u, len %u)", i,
+ vmxferpagePacket->Ranges[i].ByteOffset,
+ vmxferpagePacket->Ranges[i].ByteCount,
+ netvscPacket->PageBuffers[0].Pfn,
+ netvscPacket->PageBuffers[0].Offset,
+ netvscPacket->PageBuffers[0].Length);
+
+ /* Pass it to the upper layer */
+ ((struct netvsc_driver *)Device->Driver)->OnReceiveCallback(Device, netvscPacket);
+
+ NetVscOnReceiveCompletion(netvscPacket->Completion.Recv.ReceiveCompletionContext);
+ }
+
+ ASSERT(list_empty(&listHead));
+
+ PutNetDevice(Device);
+ DPRINT_EXIT(NETVSC);
+}
+
+static void NetVscSendReceiveCompletion(struct hv_device *Device,
+ u64 TransactionId)
+{
+ struct nvsp_message recvcompMessage;
+ int retries = 0;
+ int ret;
+
+ DPRINT_DBG(NETVSC, "Sending receive completion pkt - %llx",
+ TransactionId);
+
+ recvcompMessage.Header.MessageType =
+ NvspMessage1TypeSendRNDISPacketComplete;
+
+ /* FIXME: Pass in the status */
+ recvcompMessage.Messages.Version1Messages.SendRNDISPacketComplete.Status = NvspStatusSuccess;
+
+retry_send_cmplt:
+ /* Send the completion */
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ &recvcompMessage,
+ sizeof(struct nvsp_message),
+ TransactionId,
+ VmbusPacketTypeCompletion, 0);
+ if (ret == 0) {
+ /* success */
+ /* no-op */
+ } else if (ret == -1) {
+ /* no more room...wait a bit and attempt to retry 3 times */
+ retries++;
+ DPRINT_ERR(NETVSC, "unable to send receive completion pkt "
+ "(tid %llx)...retrying %d", TransactionId, retries);
+
+ if (retries < 4) {
+ udelay(100);
+ goto retry_send_cmplt;
+ } else {
+ DPRINT_ERR(NETVSC, "unable to send receive completion "
+ "pkt (tid %llx)...give up retrying",
+ TransactionId);
+ }
+ } else {
+ DPRINT_ERR(NETVSC, "unable to send receive completion pkt - "
+ "%llx", TransactionId);
+ }
+}
+
+/* Send a receive completion packet to RNDIS device (ie NetVsp) */
+static void NetVscOnReceiveCompletion(void *Context)
+{
+ struct hv_netvsc_packet *packet = Context;
+ struct hv_device *device = (struct hv_device *)packet->Device;
+ struct netvsc_device *netDevice;
+ u64 transactionId = 0;
+ bool fSendReceiveComp = false;
+ unsigned long flags;
+
+ DPRINT_ENTER(NETVSC);
+
+ ASSERT(packet->XferPagePacket);
+
+ /*
+ * 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.
+ */
+ netDevice = GetInboundNetDevice(device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "unable to get net device..."
+ "device being destroyed?");
+ DPRINT_EXIT(NETVSC);
+ return;
+ }
+
+ /* Overloading use of the lock. */
+ spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags);
+
+ ASSERT(packet->XferPagePacket->Count > 0);
+ packet->XferPagePacket->Count--;
+
+ /*
+ * Last one in the line that represent 1 xfer page packet.
+ * Return the xfer page packet itself to the freelist
+ */
+ if (packet->XferPagePacket->Count == 0) {
+ fSendReceiveComp = true;
+ transactionId = packet->Completion.Recv.ReceiveCompletionTid;
+ list_add_tail(&packet->XferPagePacket->ListEntry,
+ &netDevice->ReceivePacketList);
+
+ }
+
+ /* Put the packet back */
+ list_add_tail(&packet->ListEntry, &netDevice->ReceivePacketList);
+ spin_unlock_irqrestore(&netDevice->receive_packet_list_lock, flags);
+
+ /* Send a receive completion for the xfer page packet */
+ if (fSendReceiveComp)
+ NetVscSendReceiveCompletion(device, transactionId);
+
+ PutNetDevice(device);
+ DPRINT_EXIT(NETVSC);
+}
+
+void NetVscOnChannelCallback(void *Context)
+{
+ const int netPacketSize = 2048;
+ int ret;
+ struct hv_device *device = Context;
+ struct netvsc_device *netDevice;
+ u32 bytesRecvd;
+ u64 requestId;
+ unsigned char packet[netPacketSize];
+ struct vmpacket_descriptor *desc;
+ unsigned char *buffer = packet;
+ int bufferlen = netPacketSize;
+
+
+ DPRINT_ENTER(NETVSC);
+
+ ASSERT(device);
+
+ netDevice = GetInboundNetDevice(device);
+ if (!netDevice) {
+ DPRINT_ERR(NETVSC, "net device (%p) shutting down..."
+ "ignoring inbound packets", netDevice);
+ DPRINT_EXIT(NETVSC);
+ return;
+ }
+
+ do {
+ ret = device->Driver->VmbusChannelInterface.RecvPacketRaw(
+ device, buffer, bufferlen,
+ &bytesRecvd, &requestId);
+ if (ret == 0) {
+ if (bytesRecvd > 0) {
+ DPRINT_DBG(NETVSC, "receive %d bytes, tid %llx",
+ bytesRecvd, requestId);
+
+ desc = (struct vmpacket_descriptor *)buffer;
+ switch (desc->Type) {
+ case VmbusPacketTypeCompletion:
+ NetVscOnSendCompletion(device, desc);
+ break;
+
+ case VmbusPacketTypeDataUsingTransferPages:
+ NetVscOnReceive(device, desc);
+ break;
+
+ default:
+ DPRINT_ERR(NETVSC,
+ "unhandled packet type %d, "
+ "tid %llx len %d\n",
+ desc->Type, requestId,
+ bytesRecvd);
+ break;
+ }
+
+ /* reset */
+ if (bufferlen > netPacketSize) {
+ kfree(buffer);
+ buffer = packet;
+ bufferlen = netPacketSize;
+ }
+ } else {
+ /* reset */
+ if (bufferlen > netPacketSize) {
+ kfree(buffer);
+ buffer = packet;
+ bufferlen = netPacketSize;
+ }
+
+ break;
+ }
+ } else if (ret == -2) {
+ /* Handle large packet */
+ buffer = kmalloc(bytesRecvd, GFP_ATOMIC);
+ if (buffer == NULL) {
+ /* Try again next time around */
+ DPRINT_ERR(NETVSC,
+ "unable to allocate buffer of size "
+ "(%d)!!", bytesRecvd);
+ break;
+ }
+
+ bufferlen = bytesRecvd;
+ } else {
+ ASSERT(0);
+ }
+ } while (1);
+
+ PutNetDevice(device);
+ DPRINT_EXIT(NETVSC);
+ return;
+}
diff --git a/drivers/staging/hv/NetVsc.h b/drivers/staging/hv/NetVsc.h
new file mode 100644
index 000000000000..3e7112f7c755
--- /dev/null
+++ b/drivers/staging/hv/NetVsc.h
@@ -0,0 +1,329 @@
+/*
+ *
+ * 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:
+ * Hank Janssen <hjanssen@microsoft.com>
+ *
+ */
+
+
+#ifndef _NETVSC_H_
+#define _NETVSC_H_
+
+#include <linux/list.h>
+#include "VmbusPacketFormat.h"
+#include "VmbusChannelInterface.h"
+#include "NetVscApi.h"
+
+
+#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 {
+ NvspMessageTypeNone = 0,
+
+ /* Init Messages */
+ NvspMessageTypeInit = 1,
+ NvspMessageTypeInitComplete = 2,
+
+ NvspVersionMessageStart = 100,
+
+ /* Version 1 Messages */
+ NvspMessage1TypeSendNdisVersion = NvspVersionMessageStart,
+
+ NvspMessage1TypeSendReceiveBuffer,
+ NvspMessage1TypeSendReceiveBufferComplete,
+ NvspMessage1TypeRevokeReceiveBuffer,
+
+ NvspMessage1TypeSendSendBuffer,
+ NvspMessage1TypeSendSendBufferComplete,
+ NvspMessage1TypeRevokeSendBuffer,
+
+ NvspMessage1TypeSendRNDISPacket,
+ NvspMessage1TypeSendRNDISPacketComplete,
+
+ /*
+ * This should be set to the number of messages for the version with
+ * the maximum number of messages.
+ */
+ NvspNumMessagePerVersion = 9,
+};
+
+enum {
+ NvspStatusNone = 0,
+ NvspStatusSuccess,
+ NvspStatusFailure,
+ NvspStatusProtocolVersionRangeTooNew,
+ NvspStatusProtocolVersionRangeTooOld,
+ NvspStatusInvalidRndisPacket,
+ NvspStatusBusy,
+ NvspStatusMax,
+};
+
+struct nvsp_message_header {
+ u32 MessageType;
+};
+
+/* 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 MinProtocolVersion;
+ u32 MaxProtocolVersion;
+} __attribute__((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 NegotiatedProtocolVersion;
+ u32 MaximumMdlChainLength;
+ u32 Status;
+} __attribute__((packed));
+
+union nvsp_message_init_uber {
+ struct nvsp_message_init Init;
+ struct nvsp_message_init_complete InitComplete;
+} __attribute__((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 NdisMajorVersion;
+ u32 NdisMinorVersion;
+} __attribute__((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 GpadlHandle;
+ u16 Id;
+} __attribute__((packed));
+
+struct nvsp_1_receive_buffer_section {
+ u32 Offset;
+ u32 SubAllocationSize;
+ u32 NumSubAllocations;
+ u32 EndOffset;
+} __attribute__((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 NumSections;
+
+ /*
+ * 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];
+} __attribute__((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 GpadlHandle;
+ u16 Id;
+} __attribute__((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 SectionSize;
+} __attribute__((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 ChannelType;
+
+ /*
+ * 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 SendBufferSectionIndex;
+ u32 SendBufferSectionSize;
+} __attribute__((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 SendNdisVersion;
+
+ struct nvsp_1_message_send_receive_buffer SendReceiveBuffer;
+ struct nvsp_1_message_send_receive_buffer_complete
+ SendReceiveBufferComplete;
+ struct nvsp_1_message_revoke_receive_buffer RevokeReceiveBuffer;
+
+ struct nvsp_1_message_send_send_buffer SendSendBuffer;
+ struct nvsp_1_message_send_send_buffer_complete SendSendBufferComplete;
+ struct nvsp_1_message_revoke_send_buffer RevokeSendBuffer;
+
+ struct nvsp_1_message_send_rndis_packet SendRNDISPacket;
+ struct nvsp_1_message_send_rndis_packet_complete
+ SendRNDISPacketComplete;
+} __attribute__((packed));
+
+union nvsp_all_messages {
+ union nvsp_message_init_uber InitMessages;
+ union nvsp_1_message_uber Version1Messages;
+} __attribute__((packed));
+
+/* ALL Messages */
+struct nvsp_message {
+ struct nvsp_message_header Header;
+ union nvsp_all_messages Messages;
+} __attribute__((packed));
+
+
+
+
+/* #define NVSC_MIN_PROTOCOL_VERSION 1 */
+/* #define NVSC_MAX_PROTOCOL_VERSION 1 */
+
+#define NETVSC_SEND_BUFFER_SIZE (64*1024) /* 64K */
+#define NETVSC_SEND_BUFFER_ID 0xface
+
+
+#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
+
+
+/* Per netvsc channel-specific */
+struct netvsc_device {
+ struct hv_device *Device;
+
+ atomic_t RefCount;
+ atomic_t NumOutstandingSends;
+ /*
+ * List of free preallocated hv_netvsc_packet to represent receive
+ * packet
+ */
+ struct list_head ReceivePacketList;
+ spinlock_t receive_packet_list_lock;
+
+ /* Send buffer allocated by us but manages by NetVSP */
+ void *SendBuffer;
+ u32 SendBufferSize;
+ u32 SendBufferGpadlHandle;
+ u32 SendSectionSize;
+
+ /* Receive buffer allocated by us but manages by NetVSP */
+ void *ReceiveBuffer;
+ u32 ReceiveBufferSize;
+ u32 ReceiveBufferGpadlHandle;
+ u32 ReceiveSectionCount;
+ struct nvsp_1_receive_buffer_section *ReceiveSections;
+
+ /* Used for NetVSP initialization protocol */
+ struct osd_waitevent *ChannelInitEvent;
+ struct nvsp_message ChannelInitPacket;
+
+ struct nvsp_message RevokePacket;
+ /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */
+
+ /* Holds rndis device info */
+ void *Extension;
+};
+
+#endif /* _NETVSC_H_ */
diff --git a/drivers/staging/hv/NetVscApi.h b/drivers/staging/hv/NetVscApi.h
new file mode 100644
index 000000000000..1ce2b74a34a7
--- /dev/null
+++ b/drivers/staging/hv/NetVscApi.h
@@ -0,0 +1,123 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _NETVSC_API_H_
+#define _NETVSC_API_H_
+
+#include "VmbusApi.h"
+
+/* Defines */
+#define NETVSC_DEVICE_RING_BUFFER_SIZE (64*PAGE_SIZE)
+#define HW_MACADDR_LEN 6
+
+/* Fwd declaration */
+struct hv_netvsc_packet;
+
+/* Represent the xfer page packet which contains 1 or more netvsc packet */
+struct xferpage_packet {
+ struct list_head ListEntry;
+
+ /* # 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 ListEntry;
+
+ struct hv_device *Device;
+ bool IsDataPacket;
+
+ /*
+ * Valid only for receives when we break a xfer page packet
+ * into multiple netvsc packets
+ */
+ struct xferpage_packet *XferPagePacket;
+
+ union {
+ struct{
+ u64 ReceiveCompletionTid;
+ void *ReceiveCompletionContext;
+ void (*OnReceiveCompletion)(void *context);
+ } Recv;
+ struct{
+ u64 SendCompletionTid;
+ void *SendCompletionContext;
+ void (*OnSendCompletion)(void *context);
+ } Send;
+ } Completion;
+
+ /* This points to the memory after PageBuffers */
+ void *Extension;
+
+ u32 TotalDataBufferLength;
+ /* Points to the send/receive buffer where the ethernet frame is */
+ u32 PageBufferCount;
+ struct hv_page_buffer PageBuffers[NETVSC_PACKET_MAXPAGE];
+};
+
+/* Represents the net vsc driver */
+struct netvsc_driver {
+ /* Must be the first field */
+ /* Which is a bug FIXME! */
+ struct hv_driver Base;
+
+ u32 RingBufferSize;
+ u32 RequestExtSize;
+
+ /* Additional num of page buffers to allocate */
+ u32 AdditionalRequestPageBufferCount;
+
+ /*
+ * This is set by the caller to allow us to callback when we
+ * receive a packet from the "wire"
+ */
+ int (*OnReceiveCallback)(struct hv_device *dev,
+ struct hv_netvsc_packet *packet);
+ void (*OnLinkStatusChanged)(struct hv_device *dev, u32 Status);
+
+ /* Specific to this driver */
+ int (*OnOpen)(struct hv_device *dev);
+ int (*OnClose)(struct hv_device *dev);
+ int (*OnSend)(struct hv_device *dev, struct hv_netvsc_packet *packet);
+
+ void *Context;
+};
+
+struct netvsc_device_info {
+ unsigned char MacAddr[6];
+ bool LinkState; /* 0 - link up, 1 - link down */
+};
+
+/* Interface */
+int NetVscInitialize(struct hv_driver *drv);
+
+#endif /* _NETVSC_API_H_ */
diff --git a/drivers/staging/hv/RingBuffer.c b/drivers/staging/hv/RingBuffer.c
new file mode 100644
index 000000000000..f69ae33a91e3
--- /dev/null
+++ b/drivers/staging/hv/RingBuffer.c
@@ -0,0 +1,606 @@
+/*
+ *
+ * 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/mm.h>
+#include "osd.h"
+#include "logging.h"
+#include "RingBuffer.h"
+
+
+/* #defines */
+
+
+/* Amount of space to write to */
+#define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r))?((z) - ((w) - (r))):((r) - (w))
+
+
+/*++
+
+Name:
+ GetRingBufferAvailBytes()
+
+Description:
+ Get number of bytes available to read and to write to
+ for the specified ring buffer
+
+--*/
+static inline void
+GetRingBufferAvailBytes(RING_BUFFER_INFO *rbi, u32 *read, u32 *write)
+{
+ u32 read_loc,write_loc;
+
+ /* Capture the read/write indices before they changed */
+ read_loc = rbi->RingBuffer->ReadIndex;
+ write_loc = rbi->RingBuffer->WriteIndex;
+
+ *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->RingDataSize);
+ *read = rbi->RingDataSize - *write;
+}
+
+/*++
+
+Name:
+ GetNextWriteLocation()
+
+Description:
+ Get the next write location for the specified ring buffer
+
+--*/
+static inline u32
+GetNextWriteLocation(RING_BUFFER_INFO* RingInfo)
+{
+ u32 next = RingInfo->RingBuffer->WriteIndex;
+
+ ASSERT(next < RingInfo->RingDataSize);
+
+ return next;
+}
+
+/*++
+
+Name:
+ SetNextWriteLocation()
+
+Description:
+ Set the next write location for the specified ring buffer
+
+--*/
+static inline void
+SetNextWriteLocation(RING_BUFFER_INFO* RingInfo, u32 NextWriteLocation)
+{
+ RingInfo->RingBuffer->WriteIndex = NextWriteLocation;
+}
+
+/*++
+
+Name:
+ GetNextReadLocation()
+
+Description:
+ Get the next read location for the specified ring buffer
+
+--*/
+static inline u32
+GetNextReadLocation(RING_BUFFER_INFO* RingInfo)
+{
+ u32 next = RingInfo->RingBuffer->ReadIndex;
+
+ ASSERT(next < RingInfo->RingDataSize);
+
+ return next;
+}
+
+/*++
+
+Name:
+ GetNextReadLocationWithOffset()
+
+Description:
+ Get the next read location + offset for the specified ring buffer.
+ This allows the caller to skip
+
+--*/
+static inline u32
+GetNextReadLocationWithOffset(RING_BUFFER_INFO* RingInfo, u32 Offset)
+{
+ u32 next = RingInfo->RingBuffer->ReadIndex;
+
+ ASSERT(next < RingInfo->RingDataSize);
+ next += Offset;
+ next %= RingInfo->RingDataSize;
+
+ return next;
+}
+
+/*++
+
+Name:
+ SetNextReadLocation()
+
+Description:
+ Set the next read location for the specified ring buffer
+
+--*/
+static inline void
+SetNextReadLocation(RING_BUFFER_INFO* RingInfo, u32 NextReadLocation)
+{
+ RingInfo->RingBuffer->ReadIndex = NextReadLocation;
+}
+
+
+/*++
+
+Name:
+ GetRingBuffer()
+
+Description:
+ Get the start of the ring buffer
+
+--*/
+static inline void *
+GetRingBuffer(RING_BUFFER_INFO* RingInfo)
+{
+ return (void *)RingInfo->RingBuffer->Buffer;
+}
+
+
+/*++
+
+Name:
+ GetRingBufferSize()
+
+Description:
+ Get the size of the ring buffer
+
+--*/
+static inline u32
+GetRingBufferSize(RING_BUFFER_INFO* RingInfo)
+{
+ return RingInfo->RingDataSize;
+}
+
+/*++
+
+Name:
+ GetRingBufferIndices()
+
+Description:
+ Get the read and write indices as u64 of the specified ring buffer
+
+--*/
+static inline u64
+GetRingBufferIndices(RING_BUFFER_INFO* RingInfo)
+{
+ return ((u64)RingInfo->RingBuffer->WriteIndex << 32) || RingInfo->RingBuffer->ReadIndex;
+}
+
+
+/*++
+
+Name:
+ DumpRingInfo()
+
+Description:
+ Dump out to console the ring buffer info
+
+--*/
+void DumpRingInfo(RING_BUFFER_INFO *RingInfo, char *Prefix)
+{
+ u32 bytesAvailToWrite;
+ u32 bytesAvailToRead;
+
+ GetRingBufferAvailBytes(RingInfo, &bytesAvailToRead, &bytesAvailToWrite);
+
+ DPRINT(VMBUS, DEBUG_RING_LVL, "%s <<ringinfo %p buffer %p avail write %u avail read %u read idx %u write idx %u>>",
+ Prefix,
+ RingInfo,
+ RingInfo->RingBuffer->Buffer,
+ bytesAvailToWrite,
+ bytesAvailToRead,
+ RingInfo->RingBuffer->ReadIndex,
+ RingInfo->RingBuffer->WriteIndex);
+}
+
+
+/* Internal routines */
+
+static u32
+CopyToRingBuffer(
+ RING_BUFFER_INFO *RingInfo,
+ u32 StartWriteOffset,
+ void * Src,
+ u32 SrcLen);
+
+static u32
+CopyFromRingBuffer(
+ RING_BUFFER_INFO *RingInfo,
+ void * Dest,
+ u32 DestLen,
+ u32 StartReadOffset);
+
+
+
+/*++
+
+Name:
+ RingBufferGetDebugInfo()
+
+Description:
+ Get various debug metrics for the specified ring buffer
+
+--*/
+void RingBufferGetDebugInfo(RING_BUFFER_INFO *RingInfo,
+ RING_BUFFER_DEBUG_INFO *DebugInfo)
+{
+ u32 bytesAvailToWrite;
+ u32 bytesAvailToRead;
+
+ if (RingInfo->RingBuffer)
+ {
+ GetRingBufferAvailBytes(RingInfo, &bytesAvailToRead, &bytesAvailToWrite);
+
+ DebugInfo->BytesAvailToRead = bytesAvailToRead;
+ DebugInfo->BytesAvailToWrite = bytesAvailToWrite;
+ DebugInfo->CurrentReadIndex = RingInfo->RingBuffer->ReadIndex;
+ DebugInfo->CurrentWriteIndex = RingInfo->RingBuffer->WriteIndex;
+
+ DebugInfo->CurrentInterruptMask = RingInfo->RingBuffer->InterruptMask;
+ }
+}
+
+
+/*++
+
+Name:
+ GetRingBufferInterruptMask()
+
+Description:
+ Get the interrupt mask for the specified ring buffer
+
+--*/
+u32 GetRingBufferInterruptMask(RING_BUFFER_INFO *rbi)
+{
+ return rbi->RingBuffer->InterruptMask;
+}
+
+/*++
+
+Name:
+ RingBufferInit()
+
+Description:
+ Initialize the ring buffer
+
+--*/
+int RingBufferInit(RING_BUFFER_INFO *RingInfo, void *Buffer, u32 BufferLen)
+{
+ ASSERT(sizeof(RING_BUFFER) == PAGE_SIZE);
+
+ memset(RingInfo, 0, sizeof(RING_BUFFER_INFO));
+
+ RingInfo->RingBuffer = (RING_BUFFER*)Buffer;
+ RingInfo->RingBuffer->ReadIndex = RingInfo->RingBuffer->WriteIndex = 0;
+
+ RingInfo->RingSize = BufferLen;
+ RingInfo->RingDataSize = BufferLen - sizeof(RING_BUFFER);
+
+ spin_lock_init(&RingInfo->ring_lock);
+
+ return 0;
+}
+
+/*++
+
+Name:
+ RingBufferCleanup()
+
+Description:
+ Cleanup the ring buffer
+
+--*/
+void RingBufferCleanup(RING_BUFFER_INFO* RingInfo)
+{
+}
+
+/*++
+
+Name:
+ RingBufferWrite()
+
+Description:
+ Write to the ring buffer
+
+--*/
+int RingBufferWrite(RING_BUFFER_INFO *OutRingInfo,
+ struct scatterlist *sglist, u32 sgcount)
+{
+ int i=0;
+ u32 byteAvailToWrite;
+ u32 byteAvailToRead;
+ u32 totalBytesToWrite=0;
+
+ struct scatterlist *sg;
+ volatile u32 nextWriteLocation;
+ u64 prevIndices=0;
+ unsigned long flags;
+
+ DPRINT_ENTER(VMBUS);
+
+ for_each_sg(sglist, sg, sgcount, i)
+ {
+ totalBytesToWrite += sg->length;
+ }
+
+ totalBytesToWrite += sizeof(u64);
+
+ spin_lock_irqsave(&OutRingInfo->ring_lock, flags);
+
+ GetRingBufferAvailBytes(OutRingInfo, &byteAvailToRead, &byteAvailToWrite);
+
+ DPRINT_DBG(VMBUS, "Writing %u bytes...", totalBytesToWrite);
+
+ /* DumpRingInfo(OutRingInfo, "BEFORE "); */
+
+ /* If there is only room for the packet, assume it is full. Otherwise, the next time around, we think the ring buffer */
+ /* is empty since the read index == write index */
+ if (byteAvailToWrite <= totalBytesToWrite)
+ {
+ DPRINT_DBG(VMBUS, "No more space left on outbound ring buffer (needed %u, avail %u)", totalBytesToWrite, byteAvailToWrite);
+
+ spin_unlock_irqrestore(&OutRingInfo->ring_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+
+ return -1;
+ }
+
+ /* Write to the ring buffer */
+ nextWriteLocation = GetNextWriteLocation(OutRingInfo);
+
+ for_each_sg(sglist, sg, sgcount, i)
+ {
+ nextWriteLocation = CopyToRingBuffer(OutRingInfo,
+ nextWriteLocation,
+ sg_virt(sg),
+ sg->length);
+ }
+
+ /* Set previous packet start */
+ prevIndices = GetRingBufferIndices(OutRingInfo);
+
+ nextWriteLocation = CopyToRingBuffer(OutRingInfo,
+ nextWriteLocation,
+ &prevIndices,
+ sizeof(u64));
+
+ /* Make sure we flush all writes before updating the writeIndex */
+ mb();
+
+ /* Now, update the write location */
+ SetNextWriteLocation(OutRingInfo, nextWriteLocation);
+
+ /* DumpRingInfo(OutRingInfo, "AFTER "); */
+
+ spin_unlock_irqrestore(&OutRingInfo->ring_lock, flags);
+
+ DPRINT_EXIT(VMBUS);
+
+ return 0;
+}
+
+
+/*++
+
+Name:
+ RingBufferPeek()
+
+Description:
+ Read without advancing the read index
+
+--*/
+int RingBufferPeek(RING_BUFFER_INFO *InRingInfo, void *Buffer, u32 BufferLen)
+{
+ u32 bytesAvailToWrite;
+ u32 bytesAvailToRead;
+ u32 nextReadLocation=0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&InRingInfo->ring_lock, flags);
+
+ GetRingBufferAvailBytes(InRingInfo, &bytesAvailToRead, &bytesAvailToWrite);
+
+ /* Make sure there is something to read */
+ if (bytesAvailToRead < BufferLen )
+ {
+ /* DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen); */
+
+ spin_unlock_irqrestore(&InRingInfo->ring_lock, flags);
+
+ return -1;
+ }
+
+ /* Convert to byte offset */
+ nextReadLocation = GetNextReadLocation(InRingInfo);
+
+ nextReadLocation = CopyFromRingBuffer(InRingInfo,
+ Buffer,
+ BufferLen,
+ nextReadLocation);
+
+ spin_unlock_irqrestore(&InRingInfo->ring_lock, flags);
+
+ return 0;
+}
+
+
+/*++
+
+Name:
+ RingBufferRead()
+
+Description:
+ Read and advance the read index
+
+--*/
+int RingBufferRead(RING_BUFFER_INFO *InRingInfo, void *Buffer,
+ u32 BufferLen, u32 Offset)
+{
+ u32 bytesAvailToWrite;
+ u32 bytesAvailToRead;
+ u32 nextReadLocation=0;
+ u64 prevIndices=0;
+ unsigned long flags;
+
+ ASSERT(BufferLen > 0);
+
+ spin_lock_irqsave(&InRingInfo->ring_lock, flags);
+
+ GetRingBufferAvailBytes(InRingInfo, &bytesAvailToRead, &bytesAvailToWrite);
+
+ DPRINT_DBG(VMBUS, "Reading %u bytes...", BufferLen);
+
+ /* DumpRingInfo(InRingInfo, "BEFORE "); */
+
+ /* Make sure there is something to read */
+ if (bytesAvailToRead < BufferLen )
+ {
+ DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen);
+
+ spin_unlock_irqrestore(&InRingInfo->ring_lock, flags);
+
+ return -1;
+ }
+
+ nextReadLocation = GetNextReadLocationWithOffset(InRingInfo, Offset);
+
+ nextReadLocation = CopyFromRingBuffer(InRingInfo,
+ Buffer,
+ BufferLen,
+ nextReadLocation);
+
+ nextReadLocation = CopyFromRingBuffer(InRingInfo,
+ &prevIndices,
+ sizeof(u64),
+ nextReadLocation);
+
+ /* Make sure all reads are done before we update the read index since */
+ /* the writer may start writing to the read area once the read index is updated */
+ mb();
+
+ /* Update the read index */
+ SetNextReadLocation(InRingInfo, nextReadLocation);
+
+ /* DumpRingInfo(InRingInfo, "AFTER "); */
+
+ spin_unlock_irqrestore(&InRingInfo->ring_lock, flags);
+
+ return 0;
+}
+
+
+/*++
+
+Name:
+ CopyToRingBuffer()
+
+Description:
+ Helper routine to copy from source to ring buffer.
+ Assume there is enough room. Handles wrap-around in dest case only!!
+
+--*/
+static u32
+CopyToRingBuffer(
+ RING_BUFFER_INFO *RingInfo,
+ u32 StartWriteOffset,
+ void * Src,
+ u32 SrcLen)
+{
+ void * ringBuffer=GetRingBuffer(RingInfo);
+ u32 ringBufferSize=GetRingBufferSize(RingInfo);
+ u32 fragLen;
+
+ if (SrcLen > ringBufferSize - StartWriteOffset) /* wrap-around detected! */
+ {
+ DPRINT_DBG(VMBUS, "wrap-around detected!");
+
+ fragLen = ringBufferSize - StartWriteOffset;
+ memcpy(ringBuffer + StartWriteOffset, Src, fragLen);
+ memcpy(ringBuffer, Src + fragLen, SrcLen - fragLen);
+ }
+ else
+ {
+ memcpy(ringBuffer + StartWriteOffset, Src, SrcLen);
+ }
+
+ StartWriteOffset += SrcLen;
+ StartWriteOffset %= ringBufferSize;
+
+ return StartWriteOffset;
+}
+
+
+/*++
+
+Name:
+ CopyFromRingBuffer()
+
+Description:
+ Helper routine to copy to source from ring buffer.
+ Assume there is enough room. Handles wrap-around in src case only!!
+
+--*/
+static u32
+CopyFromRingBuffer(
+ RING_BUFFER_INFO *RingInfo,
+ void * Dest,
+ u32 DestLen,
+ u32 StartReadOffset)
+{
+ void * ringBuffer=GetRingBuffer(RingInfo);
+ u32 ringBufferSize=GetRingBufferSize(RingInfo);
+
+ u32 fragLen;
+
+ if (DestLen > ringBufferSize - StartReadOffset) /* wrap-around detected at the src */
+ {
+ DPRINT_DBG(VMBUS, "src wrap-around detected!");
+
+ fragLen = ringBufferSize - StartReadOffset;
+
+ memcpy(Dest, ringBuffer + StartReadOffset, fragLen);
+ memcpy(Dest + fragLen, ringBuffer, DestLen - fragLen);
+ }
+ else
+ {
+ memcpy(Dest, ringBuffer + StartReadOffset, DestLen);
+ }
+
+ StartReadOffset += DestLen;
+ StartReadOffset %= ringBufferSize;
+
+ return StartReadOffset;
+}
+
+
+/* eof */
diff --git a/drivers/staging/hv/RingBuffer.h b/drivers/staging/hv/RingBuffer.h
new file mode 100644
index 000000000000..6202157e145d
--- /dev/null
+++ b/drivers/staging/hv/RingBuffer.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _RING_BUFFER_H_
+#define _RING_BUFFER_H_
+
+#include <linux/scatterlist.h>
+
+typedef struct _RING_BUFFER {
+ /* Offset in bytes from the start of ring data below */
+ volatile u32 WriteIndex;
+
+ /* Offset in bytes from the start of ring data below */
+ volatile u32 ReadIndex;
+
+ volatile u32 InterruptMask;
+
+ /* Pad it to PAGE_SIZE so that data starts on page boundary */
+ u8 Reserved[4084];
+
+ /* NOTE:
+ * The InterruptMask field is used only for channels but since our
+ * vmbus connection also uses this data structure and its data starts
+ * here, we commented out this field.
+ */
+ /* volatile u32 InterruptMask; */
+
+ /*
+ * Ring data starts here + RingDataStartOffset
+ * !!! DO NOT place any fields below this !!!
+ */
+ u8 Buffer[0];
+} __attribute__((packed)) RING_BUFFER;
+
+typedef struct _RING_BUFFER_INFO {
+ RING_BUFFER *RingBuffer;
+ u32 RingSize; /* Include the shared header */
+ spinlock_t ring_lock;
+
+ u32 RingDataSize; /* < ringSize */
+ u32 RingDataStartOffset;
+
+} RING_BUFFER_INFO;
+
+typedef struct _RING_BUFFER_DEBUG_INFO {
+ u32 CurrentInterruptMask;
+ u32 CurrentReadIndex;
+ u32 CurrentWriteIndex;
+ u32 BytesAvailToRead;
+ u32 BytesAvailToWrite;
+} RING_BUFFER_DEBUG_INFO;
+
+
+
+/* Interface */
+
+
+int RingBufferInit(RING_BUFFER_INFO *RingInfo, void *Buffer, u32 BufferLen);
+
+void RingBufferCleanup(RING_BUFFER_INFO *RingInfo);
+
+int RingBufferWrite(RING_BUFFER_INFO *RingInfo,
+ struct scatterlist *sglist,
+ u32 sgcount);
+
+int RingBufferPeek(RING_BUFFER_INFO *RingInfo, void *Buffer, u32 BufferLen);
+
+int RingBufferRead(RING_BUFFER_INFO *RingInfo,
+ void *Buffer,
+ u32 BufferLen,
+ u32 Offset);
+
+u32 GetRingBufferInterruptMask(RING_BUFFER_INFO *RingInfo);
+
+void DumpRingInfo(RING_BUFFER_INFO *RingInfo, char *Prefix);
+
+void RingBufferGetDebugInfo(RING_BUFFER_INFO *RingInfo,
+ RING_BUFFER_DEBUG_INFO *DebugInfo);
+
+#endif /* _RING_BUFFER_H_ */
diff --git a/drivers/staging/hv/RndisFilter.c b/drivers/staging/hv/RndisFilter.c
new file mode 100644
index 000000000000..26d79975387c
--- /dev/null
+++ b/drivers/staging/hv/RndisFilter.c
@@ -0,0 +1,1000 @@
+/*
+ * 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/highmem.h>
+#include <linux/io.h>
+#include "osd.h"
+#include "logging.h"
+#include "NetVscApi.h"
+#include "RndisFilter.h"
+
+/* Data types */
+struct rndis_filter_driver_object {
+ /* The original driver */
+ struct netvsc_driver InnerDriver;
+};
+
+enum rndis_device_state {
+ RNDIS_DEV_UNINITIALIZED = 0,
+ RNDIS_DEV_INITIALIZING,
+ RNDIS_DEV_INITIALIZED,
+ RNDIS_DEV_DATAINITIALIZED,
+};
+
+struct rndis_device {
+ struct netvsc_device *NetDevice;
+
+ enum rndis_device_state State;
+ u32 LinkStatus;
+ atomic_t NewRequestId;
+
+ spinlock_t request_lock;
+ struct list_head RequestList;
+
+ unsigned char HwMacAddr[HW_MACADDR_LEN];
+};
+
+struct rndis_request {
+ struct list_head ListEntry;
+ struct osd_waitevent *WaitEvent;
+
+ /*
+ * 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 ResponseMessage;
+
+ /* Simplify allocation by having a netvsc packet inline */
+ struct hv_netvsc_packet Packet;
+ struct hv_page_buffer Buffer;
+ /* FIXME: We assumed a fixed size request here. */
+ struct rndis_message RequestMessage;
+};
+
+
+struct rndis_filter_packet {
+ void *CompletionContext;
+ void (*OnCompletion)(void *context);
+ struct rndis_message Message;
+};
+
+
+static int RndisFilterOnDeviceAdd(struct hv_device *Device,
+ void *AdditionalInfo);
+
+static int RndisFilterOnDeviceRemove(struct hv_device *Device);
+
+static void RndisFilterOnCleanup(struct hv_driver *Driver);
+
+static int RndisFilterOnOpen(struct hv_device *Device);
+
+static int RndisFilterOnClose(struct hv_device *Device);
+
+static int RndisFilterOnSend(struct hv_device *Device,
+ struct hv_netvsc_packet *Packet);
+
+static void RndisFilterOnSendCompletion(void *Context);
+
+static void RndisFilterOnSendRequestCompletion(void *Context);
+
+
+/* The one and only */
+static struct rndis_filter_driver_object gRndisFilter;
+
+static struct rndis_device *GetRndisDevice(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->RequestList);
+
+ device->State = RNDIS_DEV_UNINITIALIZED;
+
+ return device;
+}
+
+static struct rndis_request *GetRndisRequest(struct rndis_device *Device,
+ u32 MessageType,
+ u32 MessageLength)
+{
+ struct rndis_request *request;
+ struct rndis_message *rndisMessage;
+ struct rndis_set_request *set;
+ unsigned long flags;
+
+ request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
+ if (!request)
+ return NULL;
+
+ request->WaitEvent = osd_WaitEventCreate();
+ if (!request->WaitEvent) {
+ kfree(request);
+ return NULL;
+ }
+
+ rndisMessage = &request->RequestMessage;
+ rndisMessage->NdisMessageType = MessageType;
+ rndisMessage->MessageLength = MessageLength;
+
+ /*
+ * 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 = &rndisMessage->Message.SetRequest;
+ set->RequestId = atomic_inc_return(&Device->NewRequestId);
+
+ /* Add to the request list */
+ spin_lock_irqsave(&Device->request_lock, flags);
+ list_add_tail(&request->ListEntry, &Device->RequestList);
+ spin_unlock_irqrestore(&Device->request_lock, flags);
+
+ return request;
+}
+
+static void PutRndisRequest(struct rndis_device *Device,
+ struct rndis_request *Request)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&Device->request_lock, flags);
+ list_del(&Request->ListEntry);
+ spin_unlock_irqrestore(&Device->request_lock, flags);
+
+ kfree(Request->WaitEvent);
+ kfree(Request);
+}
+
+static void DumpRndisMessage(struct rndis_message *RndisMessage)
+{
+ switch (RndisMessage->NdisMessageType) {
+ case REMOTE_NDIS_PACKET_MSG:
+ DPRINT_DBG(NETVSC, "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",
+ RndisMessage->MessageLength,
+ RndisMessage->Message.Packet.DataOffset,
+ RndisMessage->Message.Packet.DataLength,
+ RndisMessage->Message.Packet.NumOOBDataElements,
+ RndisMessage->Message.Packet.OOBDataOffset,
+ RndisMessage->Message.Packet.OOBDataLength,
+ RndisMessage->Message.Packet.PerPacketInfoOffset,
+ RndisMessage->Message.Packet.PerPacketInfoLength);
+ break;
+
+ case REMOTE_NDIS_INITIALIZE_CMPLT:
+ DPRINT_DBG(NETVSC, "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)",
+ RndisMessage->MessageLength,
+ RndisMessage->Message.InitializeComplete.RequestId,
+ RndisMessage->Message.InitializeComplete.Status,
+ RndisMessage->Message.InitializeComplete.MajorVersion,
+ RndisMessage->Message.InitializeComplete.MinorVersion,
+ RndisMessage->Message.InitializeComplete.DeviceFlags,
+ RndisMessage->Message.InitializeComplete.MaxTransferSize,
+ RndisMessage->Message.InitializeComplete.MaxPacketsPerMessage,
+ RndisMessage->Message.InitializeComplete.PacketAlignmentFactor);
+ break;
+
+ case REMOTE_NDIS_QUERY_CMPLT:
+ DPRINT_DBG(NETVSC, "REMOTE_NDIS_QUERY_CMPLT "
+ "(len %u, id 0x%x, status 0x%x, buf len %u, "
+ "buf offset %u)",
+ RndisMessage->MessageLength,
+ RndisMessage->Message.QueryComplete.RequestId,
+ RndisMessage->Message.QueryComplete.Status,
+ RndisMessage->Message.QueryComplete.InformationBufferLength,
+ RndisMessage->Message.QueryComplete.InformationBufferOffset);
+ break;
+
+ case REMOTE_NDIS_SET_CMPLT:
+ DPRINT_DBG(NETVSC,
+ "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)",
+ RndisMessage->MessageLength,
+ RndisMessage->Message.SetComplete.RequestId,
+ RndisMessage->Message.SetComplete.Status);
+ break;
+
+ case REMOTE_NDIS_INDICATE_STATUS_MSG:
+ DPRINT_DBG(NETVSC, "REMOTE_NDIS_INDICATE_STATUS_MSG "
+ "(len %u, status 0x%x, buf len %u, buf offset %u)",
+ RndisMessage->MessageLength,
+ RndisMessage->Message.IndicateStatus.Status,
+ RndisMessage->Message.IndicateStatus.StatusBufferLength,
+ RndisMessage->Message.IndicateStatus.StatusBufferOffset);
+ break;
+
+ default:
+ DPRINT_DBG(NETVSC, "0x%x (len %u)",
+ RndisMessage->NdisMessageType,
+ RndisMessage->MessageLength);
+ break;
+ }
+}
+
+static int RndisFilterSendRequest(struct rndis_device *Device,
+ struct rndis_request *Request)
+{
+ int ret;
+ struct hv_netvsc_packet *packet;
+
+ DPRINT_ENTER(NETVSC);
+
+ /* Setup the packet to send it */
+ packet = &Request->Packet;
+
+ packet->IsDataPacket = false;
+ packet->TotalDataBufferLength = Request->RequestMessage.MessageLength;
+ packet->PageBufferCount = 1;
+
+ packet->PageBuffers[0].Pfn = virt_to_phys(&Request->RequestMessage) >>
+ PAGE_SHIFT;
+ packet->PageBuffers[0].Length = Request->RequestMessage.MessageLength;
+ packet->PageBuffers[0].Offset =
+ (unsigned long)&Request->RequestMessage & (PAGE_SIZE - 1);
+
+ packet->Completion.Send.SendCompletionContext = Request;/* packet; */
+ packet->Completion.Send.OnSendCompletion =
+ RndisFilterOnSendRequestCompletion;
+ packet->Completion.Send.SendCompletionTid = (unsigned long)Device;
+
+ ret = gRndisFilter.InnerDriver.OnSend(Device->NetDevice->Device, packet);
+ DPRINT_EXIT(NETVSC);
+ return ret;
+}
+
+static void RndisFilterReceiveResponse(struct rndis_device *Device,
+ struct rndis_message *Response)
+{
+ struct rndis_request *request = NULL;
+ bool found = false;
+ unsigned long flags;
+
+ DPRINT_ENTER(NETVSC);
+
+ spin_lock_irqsave(&Device->request_lock, flags);
+ list_for_each_entry(request, &Device->RequestList, ListEntry) {
+ /*
+ * All request/response message contains RequestId as the 1st
+ * field
+ */
+ if (request->RequestMessage.Message.InitializeRequest.RequestId
+ == Response->Message.InitializeComplete.RequestId) {
+ DPRINT_DBG(NETVSC, "found rndis request for "
+ "this response (id 0x%x req type 0x%x res "
+ "type 0x%x)",
+ request->RequestMessage.Message.InitializeRequest.RequestId,
+ request->RequestMessage.NdisMessageType,
+ Response->NdisMessageType);
+
+ found = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&Device->request_lock, flags);
+
+ if (found) {
+ if (Response->MessageLength <= sizeof(struct rndis_message)) {
+ memcpy(&request->ResponseMessage, Response,
+ Response->MessageLength);
+ } else {
+ DPRINT_ERR(NETVSC, "rndis response buffer overflow "
+ "detected (size %u max %zu)",
+ Response->MessageLength,
+ sizeof(struct rndis_filter_packet));
+
+ if (Response->NdisMessageType ==
+ REMOTE_NDIS_RESET_CMPLT) {
+ /* does not have a request id field */
+ request->ResponseMessage.Message.ResetComplete.Status = STATUS_BUFFER_OVERFLOW;
+ } else {
+ request->ResponseMessage.Message.InitializeComplete.Status = STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ osd_WaitEventSet(request->WaitEvent);
+ } else {
+ DPRINT_ERR(NETVSC, "no rndis request found for this response "
+ "(id 0x%x res type 0x%x)",
+ Response->Message.InitializeComplete.RequestId,
+ Response->NdisMessageType);
+ }
+
+ DPRINT_EXIT(NETVSC);
+}
+
+static void RndisFilterReceiveIndicateStatus(struct rndis_device *Device,
+ struct rndis_message *Response)
+{
+ struct rndis_indicate_status *indicate =
+ &Response->Message.IndicateStatus;
+
+ if (indicate->Status == RNDIS_STATUS_MEDIA_CONNECT) {
+ gRndisFilter.InnerDriver.OnLinkStatusChanged(Device->NetDevice->Device, 1);
+ } else if (indicate->Status == RNDIS_STATUS_MEDIA_DISCONNECT) {
+ gRndisFilter.InnerDriver.OnLinkStatusChanged(Device->NetDevice->Device, 0);
+ } else {
+ /*
+ * TODO:
+ */
+ }
+}
+
+static void RndisFilterReceiveData(struct rndis_device *Device,
+ struct rndis_message *Message,
+ struct hv_netvsc_packet *Packet)
+{
+ struct rndis_packet *rndisPacket;
+ u32 dataOffset;
+
+ DPRINT_ENTER(NETVSC);
+
+ /* empty ethernet frame ?? */
+ ASSERT(Packet->PageBuffers[0].Length >
+ RNDIS_MESSAGE_SIZE(struct rndis_packet));
+
+ rndisPacket = &Message->Message.Packet;
+
+ /*
+ * 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 */
+ dataOffset = RNDIS_HEADER_SIZE + rndisPacket->DataOffset;
+
+ Packet->TotalDataBufferLength -= dataOffset;
+ Packet->PageBuffers[0].Offset += dataOffset;
+ Packet->PageBuffers[0].Length -= dataOffset;
+
+ Packet->IsDataPacket = true;
+
+ gRndisFilter.InnerDriver.OnReceiveCallback(Device->NetDevice->Device,
+ Packet);
+
+ DPRINT_EXIT(NETVSC);
+}
+
+static int RndisFilterOnReceive(struct hv_device *Device,
+ struct hv_netvsc_packet *Packet)
+{
+ struct netvsc_device *netDevice = Device->Extension;
+ struct rndis_device *rndisDevice;
+ struct rndis_message rndisMessage;
+ struct rndis_message *rndisHeader;
+
+ DPRINT_ENTER(NETVSC);
+
+ ASSERT(netDevice);
+ /* Make sure the rndis device state is initialized */
+ if (!netDevice->Extension) {
+ DPRINT_ERR(NETVSC, "got rndis message but no rndis device..."
+ "dropping this message!");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+
+ rndisDevice = (struct rndis_device *)netDevice->Extension;
+ if (rndisDevice->State == RNDIS_DEV_UNINITIALIZED) {
+ DPRINT_ERR(NETVSC, "got rndis message but rndis device "
+ "uninitialized...dropping this message!");
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+
+ rndisHeader = (struct rndis_message *)kmap_atomic(
+ pfn_to_page(Packet->PageBuffers[0].Pfn), KM_IRQ0);
+
+ rndisHeader = (void *)((unsigned long)rndisHeader +
+ Packet->PageBuffers[0].Offset);
+
+ /* Make sure we got a valid rndis message */
+ /*
+ * FIXME: There seems to be a bug in set completion msg where its
+ * MessageLength is 16 bytes but the ByteCount field in the xfer page
+ * range shows 52 bytes
+ * */
+#if 0
+ if (Packet->TotalDataBufferLength != rndisHeader->MessageLength) {
+ kunmap_atomic(rndisHeader - Packet->PageBuffers[0].Offset,
+ KM_IRQ0);
+
+ DPRINT_ERR(NETVSC, "invalid rndis message? (expected %u "
+ "bytes got %u)...dropping this message!",
+ rndisHeader->MessageLength,
+ Packet->TotalDataBufferLength);
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+#endif
+
+ if ((rndisHeader->NdisMessageType != REMOTE_NDIS_PACKET_MSG) &&
+ (rndisHeader->MessageLength > sizeof(struct rndis_message))) {
+ DPRINT_ERR(NETVSC, "incoming rndis message buffer overflow "
+ "detected (got %u, max %zu)...marking it an error!",
+ rndisHeader->MessageLength,
+ sizeof(struct rndis_message));
+ }
+
+ memcpy(&rndisMessage, rndisHeader,
+ (rndisHeader->MessageLength > sizeof(struct rndis_message)) ?
+ sizeof(struct rndis_message) :
+ rndisHeader->MessageLength);
+
+ kunmap_atomic(rndisHeader - Packet->PageBuffers[0].Offset, KM_IRQ0);
+
+ DumpRndisMessage(&rndisMessage);
+
+ switch (rndisMessage.NdisMessageType) {
+ case REMOTE_NDIS_PACKET_MSG:
+ /* data msg */
+ RndisFilterReceiveData(rndisDevice, &rndisMessage, Packet);
+ break;
+
+ case REMOTE_NDIS_INITIALIZE_CMPLT:
+ case REMOTE_NDIS_QUERY_CMPLT:
+ case REMOTE_NDIS_SET_CMPLT:
+ /* case REMOTE_NDIS_RESET_CMPLT: */
+ /* case REMOTE_NDIS_KEEPALIVE_CMPLT: */
+ /* completion msgs */
+ RndisFilterReceiveResponse(rndisDevice, &rndisMessage);
+ break;
+
+ case REMOTE_NDIS_INDICATE_STATUS_MSG:
+ /* notification msgs */
+ RndisFilterReceiveIndicateStatus(rndisDevice, &rndisMessage);
+ break;
+ default:
+ DPRINT_ERR(NETVSC, "unhandled rndis message (type %u len %u)",
+ rndisMessage.NdisMessageType,
+ rndisMessage.MessageLength);
+ break;
+ }
+
+ DPRINT_EXIT(NETVSC);
+ return 0;
+}
+
+static int RndisFilterQueryDevice(struct rndis_device *Device, u32 Oid,
+ void *Result, u32 *ResultSize)
+{
+ struct rndis_request *request;
+ u32 inresultSize = *ResultSize;
+ struct rndis_query_request *query;
+ struct rndis_query_complete *queryComplete;
+ int ret = 0;
+
+ DPRINT_ENTER(NETVSC);
+
+ ASSERT(Result);
+
+ *ResultSize = 0;
+ request = GetRndisRequest(Device, REMOTE_NDIS_QUERY_MSG,
+ RNDIS_MESSAGE_SIZE(struct rndis_query_request));
+ if (!request) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ /* Setup the rndis query */
+ query = &request->RequestMessage.Message.QueryRequest;
+ query->Oid = Oid;
+ query->InformationBufferOffset = sizeof(struct rndis_query_request);
+ query->InformationBufferLength = 0;
+ query->DeviceVcHandle = 0;
+
+ ret = RndisFilterSendRequest(Device, request);
+ if (ret != 0)
+ goto Cleanup;
+
+ osd_WaitEventWait(request->WaitEvent);
+
+ /* Copy the response back */
+ queryComplete = &request->ResponseMessage.Message.QueryComplete;
+
+ if (queryComplete->InformationBufferLength > inresultSize) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ memcpy(Result,
+ (void *)((unsigned long)queryComplete +
+ queryComplete->InformationBufferOffset),
+ queryComplete->InformationBufferLength);
+
+ *ResultSize = queryComplete->InformationBufferLength;
+
+Cleanup:
+ if (request)
+ PutRndisRequest(Device, request);
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static int RndisFilterQueryDeviceMac(struct rndis_device *Device)
+{
+ u32 size = HW_MACADDR_LEN;
+
+ return RndisFilterQueryDevice(Device,
+ RNDIS_OID_802_3_PERMANENT_ADDRESS,
+ Device->HwMacAddr, &size);
+}
+
+static int RndisFilterQueryDeviceLinkStatus(struct rndis_device *Device)
+{
+ u32 size = sizeof(u32);
+
+ return RndisFilterQueryDevice(Device,
+ RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
+ &Device->LinkStatus, &size);
+}
+
+static int RndisFilterSetPacketFilter(struct rndis_device *Device,
+ u32 NewFilter)
+{
+ struct rndis_request *request;
+ struct rndis_set_request *set;
+ struct rndis_set_complete *setComplete;
+ u32 status;
+ int ret;
+
+ DPRINT_ENTER(NETVSC);
+
+ ASSERT(RNDIS_MESSAGE_SIZE(struct rndis_set_request) + sizeof(u32) <=
+ sizeof(struct rndis_message));
+
+ request = GetRndisRequest(Device, REMOTE_NDIS_SET_MSG,
+ RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
+ sizeof(u32));
+ if (!request) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ /* Setup the rndis set */
+ set = &request->RequestMessage.Message.SetRequest;
+ set->Oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
+ set->InformationBufferLength = sizeof(u32);
+ set->InformationBufferOffset = sizeof(struct rndis_set_request);
+
+ memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
+ &NewFilter, sizeof(u32));
+
+ ret = RndisFilterSendRequest(Device, request);
+ if (ret != 0)
+ goto Cleanup;
+
+ ret = osd_WaitEventWaitEx(request->WaitEvent, 2000/*2sec*/);
+ if (!ret) {
+ ret = -1;
+ DPRINT_ERR(NETVSC, "timeout before we got a set response...");
+ /*
+ * We cant deallocate the request since we may still receive a
+ * send completion for it.
+ */
+ goto Exit;
+ } else {
+ if (ret > 0)
+ ret = 0;
+ setComplete = &request->ResponseMessage.Message.SetComplete;
+ status = setComplete->Status;
+ }
+
+Cleanup:
+ if (request)
+ PutRndisRequest(Device, request);
+Exit:
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+int RndisFilterInit(struct netvsc_driver *Driver)
+{
+ DPRINT_ENTER(NETVSC);
+
+ DPRINT_DBG(NETVSC, "sizeof(struct rndis_filter_packet) == %zd",
+ sizeof(struct rndis_filter_packet));
+
+ Driver->RequestExtSize = sizeof(struct rndis_filter_packet);
+ Driver->AdditionalRequestPageBufferCount = 1; /* For rndis header */
+
+ /* Driver->Context = rndisDriver; */
+
+ memset(&gRndisFilter, 0, sizeof(struct rndis_filter_driver_object));
+
+ /*rndisDriver->Driver = Driver;
+
+ ASSERT(Driver->OnLinkStatusChanged);
+ rndisDriver->OnLinkStatusChanged = Driver->OnLinkStatusChanged;*/
+
+ /* Save the original dispatch handlers before we override it */
+ gRndisFilter.InnerDriver.Base.OnDeviceAdd = Driver->Base.OnDeviceAdd;
+ gRndisFilter.InnerDriver.Base.OnDeviceRemove =
+ Driver->Base.OnDeviceRemove;
+ gRndisFilter.InnerDriver.Base.OnCleanup = Driver->Base.OnCleanup;
+
+ ASSERT(Driver->OnSend);
+ ASSERT(Driver->OnReceiveCallback);
+ gRndisFilter.InnerDriver.OnSend = Driver->OnSend;
+ gRndisFilter.InnerDriver.OnReceiveCallback = Driver->OnReceiveCallback;
+ gRndisFilter.InnerDriver.OnLinkStatusChanged =
+ Driver->OnLinkStatusChanged;
+
+ /* Override */
+ Driver->Base.OnDeviceAdd = RndisFilterOnDeviceAdd;
+ Driver->Base.OnDeviceRemove = RndisFilterOnDeviceRemove;
+ Driver->Base.OnCleanup = RndisFilterOnCleanup;
+ Driver->OnSend = RndisFilterOnSend;
+ Driver->OnOpen = RndisFilterOnOpen;
+ Driver->OnClose = RndisFilterOnClose;
+ /* Driver->QueryLinkStatus = RndisFilterQueryDeviceLinkStatus; */
+ Driver->OnReceiveCallback = RndisFilterOnReceive;
+
+ DPRINT_EXIT(NETVSC);
+
+ return 0;
+}
+
+static int RndisFilterInitDevice(struct rndis_device *Device)
+{
+ struct rndis_request *request;
+ struct rndis_initialize_request *init;
+ struct rndis_initialize_complete *initComplete;
+ u32 status;
+ int ret;
+
+ DPRINT_ENTER(NETVSC);
+
+ request = GetRndisRequest(Device, REMOTE_NDIS_INITIALIZE_MSG,
+ RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
+ if (!request) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ /* Setup the rndis set */
+ init = &request->RequestMessage.Message.InitializeRequest;
+ init->MajorVersion = RNDIS_MAJOR_VERSION;
+ init->MinorVersion = RNDIS_MINOR_VERSION;
+ /* FIXME: Use 1536 - rounded ethernet frame size */
+ init->MaxTransferSize = 2048;
+
+ Device->State = RNDIS_DEV_INITIALIZING;
+
+ ret = RndisFilterSendRequest(Device, request);
+ if (ret != 0) {
+ Device->State = RNDIS_DEV_UNINITIALIZED;
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(request->WaitEvent);
+
+ initComplete = &request->ResponseMessage.Message.InitializeComplete;
+ status = initComplete->Status;
+ if (status == RNDIS_STATUS_SUCCESS) {
+ Device->State = RNDIS_DEV_INITIALIZED;
+ ret = 0;
+ } else {
+ Device->State = RNDIS_DEV_UNINITIALIZED;
+ ret = -1;
+ }
+
+Cleanup:
+ if (request)
+ PutRndisRequest(Device, request);
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static void RndisFilterHaltDevice(struct rndis_device *Device)
+{
+ struct rndis_request *request;
+ struct rndis_halt_request *halt;
+
+ DPRINT_ENTER(NETVSC);
+
+ /* Attempt to do a rndis device halt */
+ request = GetRndisRequest(Device, REMOTE_NDIS_HALT_MSG,
+ RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
+ if (!request)
+ goto Cleanup;
+
+ /* Setup the rndis set */
+ halt = &request->RequestMessage.Message.HaltRequest;
+ halt->RequestId = atomic_inc_return(&Device->NewRequestId);
+
+ /* Ignore return since this msg is optional. */
+ RndisFilterSendRequest(Device, request);
+
+ Device->State = RNDIS_DEV_UNINITIALIZED;
+
+Cleanup:
+ if (request)
+ PutRndisRequest(Device, request);
+ DPRINT_EXIT(NETVSC);
+ return;
+}
+
+static int RndisFilterOpenDevice(struct rndis_device *Device)
+{
+ int ret;
+
+ DPRINT_ENTER(NETVSC);
+
+ if (Device->State != RNDIS_DEV_INITIALIZED)
+ return 0;
+
+ ret = RndisFilterSetPacketFilter(Device,
+ NDIS_PACKET_TYPE_BROADCAST |
+ NDIS_PACKET_TYPE_DIRECTED);
+ if (ret == 0)
+ Device->State = RNDIS_DEV_DATAINITIALIZED;
+
+ DPRINT_EXIT(NETVSC);
+ return ret;
+}
+
+static int RndisFilterCloseDevice(struct rndis_device *Device)
+{
+ int ret;
+
+ DPRINT_ENTER(NETVSC);
+
+ if (Device->State != RNDIS_DEV_DATAINITIALIZED)
+ return 0;
+
+ ret = RndisFilterSetPacketFilter(Device, 0);
+ if (ret == 0)
+ Device->State = RNDIS_DEV_INITIALIZED;
+
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static int RndisFilterOnDeviceAdd(struct hv_device *Device,
+ void *AdditionalInfo)
+{
+ int ret;
+ struct netvsc_device *netDevice;
+ struct rndis_device *rndisDevice;
+ struct netvsc_device_info *deviceInfo = AdditionalInfo;
+
+ DPRINT_ENTER(NETVSC);
+
+ rndisDevice = GetRndisDevice();
+ if (!rndisDevice) {
+ DPRINT_EXIT(NETVSC);
+ return -1;
+ }
+
+ DPRINT_DBG(NETVSC, "rndis device object allocated - %p", rndisDevice);
+
+ /*
+ * 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 = gRndisFilter.InnerDriver.Base.OnDeviceAdd(Device, AdditionalInfo);
+ if (ret != 0) {
+ kfree(rndisDevice);
+ DPRINT_EXIT(NETVSC);
+ return ret;
+ }
+
+
+ /* Initialize the rndis device */
+ netDevice = Device->Extension;
+ ASSERT(netDevice);
+ ASSERT(netDevice->Device);
+
+ netDevice->Extension = rndisDevice;
+ rndisDevice->NetDevice = netDevice;
+
+ /* Send the rndis initialization message */
+ ret = RndisFilterInitDevice(rndisDevice);
+ if (ret != 0) {
+ /*
+ * TODO: If rndis init failed, we will need to shut down the
+ * channel
+ */
+ }
+
+ /* Get the mac address */
+ ret = RndisFilterQueryDeviceMac(rndisDevice);
+ if (ret != 0) {
+ /*
+ * TODO: shutdown rndis device and the channel
+ */
+ }
+
+ DPRINT_INFO(NETVSC, "Device 0x%p mac addr %02x%02x%02x%02x%02x%02x",
+ rndisDevice,
+ rndisDevice->HwMacAddr[0],
+ rndisDevice->HwMacAddr[1],
+ rndisDevice->HwMacAddr[2],
+ rndisDevice->HwMacAddr[3],
+ rndisDevice->HwMacAddr[4],
+ rndisDevice->HwMacAddr[5]);
+
+ memcpy(deviceInfo->MacAddr, rndisDevice->HwMacAddr, HW_MACADDR_LEN);
+
+ RndisFilterQueryDeviceLinkStatus(rndisDevice);
+
+ deviceInfo->LinkState = rndisDevice->LinkStatus;
+ DPRINT_INFO(NETVSC, "Device 0x%p link state %s", rndisDevice,
+ ((deviceInfo->LinkState) ? ("down") : ("up")));
+
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static int RndisFilterOnDeviceRemove(struct hv_device *Device)
+{
+ struct netvsc_device *netDevice = Device->Extension;
+ struct rndis_device *rndisDevice = netDevice->Extension;
+
+ DPRINT_ENTER(NETVSC);
+
+ /* Halt and release the rndis device */
+ RndisFilterHaltDevice(rndisDevice);
+
+ kfree(rndisDevice);
+ netDevice->Extension = NULL;
+
+ /* Pass control to inner driver to remove the device */
+ gRndisFilter.InnerDriver.Base.OnDeviceRemove(Device);
+
+ DPRINT_EXIT(NETVSC);
+
+ return 0;
+}
+
+static void RndisFilterOnCleanup(struct hv_driver *Driver)
+{
+ DPRINT_ENTER(NETVSC);
+
+ DPRINT_EXIT(NETVSC);
+}
+
+static int RndisFilterOnOpen(struct hv_device *Device)
+{
+ int ret;
+ struct netvsc_device *netDevice = Device->Extension;
+
+ DPRINT_ENTER(NETVSC);
+
+ ASSERT(netDevice);
+ ret = RndisFilterOpenDevice(netDevice->Extension);
+
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static int RndisFilterOnClose(struct hv_device *Device)
+{
+ int ret;
+ struct netvsc_device *netDevice = Device->Extension;
+
+ DPRINT_ENTER(NETVSC);
+
+ ASSERT(netDevice);
+ ret = RndisFilterCloseDevice(netDevice->Extension);
+
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static int RndisFilterOnSend(struct hv_device *Device,
+ struct hv_netvsc_packet *Packet)
+{
+ int ret;
+ struct rndis_filter_packet *filterPacket;
+ struct rndis_message *rndisMessage;
+ struct rndis_packet *rndisPacket;
+ u32 rndisMessageSize;
+
+ DPRINT_ENTER(NETVSC);
+
+ /* Add the rndis header */
+ filterPacket = (struct rndis_filter_packet *)Packet->Extension;
+ ASSERT(filterPacket);
+
+ memset(filterPacket, 0, sizeof(struct rndis_filter_packet));
+
+ rndisMessage = &filterPacket->Message;
+ rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet);
+
+ rndisMessage->NdisMessageType = REMOTE_NDIS_PACKET_MSG;
+ rndisMessage->MessageLength = Packet->TotalDataBufferLength +
+ rndisMessageSize;
+
+ rndisPacket = &rndisMessage->Message.Packet;
+ rndisPacket->DataOffset = sizeof(struct rndis_packet);
+ rndisPacket->DataLength = Packet->TotalDataBufferLength;
+
+ Packet->IsDataPacket = true;
+ Packet->PageBuffers[0].Pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT;
+ Packet->PageBuffers[0].Offset =
+ (unsigned long)rndisMessage & (PAGE_SIZE-1);
+ Packet->PageBuffers[0].Length = rndisMessageSize;
+
+ /* Save the packet send completion and context */
+ filterPacket->OnCompletion = Packet->Completion.Send.OnSendCompletion;
+ filterPacket->CompletionContext =
+ Packet->Completion.Send.SendCompletionContext;
+
+ /* Use ours */
+ Packet->Completion.Send.OnSendCompletion = RndisFilterOnSendCompletion;
+ Packet->Completion.Send.SendCompletionContext = filterPacket;
+
+ ret = gRndisFilter.InnerDriver.OnSend(Device, Packet);
+ if (ret != 0) {
+ /*
+ * Reset the completion to originals to allow retries from
+ * above
+ */
+ Packet->Completion.Send.OnSendCompletion =
+ filterPacket->OnCompletion;
+ Packet->Completion.Send.SendCompletionContext =
+ filterPacket->CompletionContext;
+ }
+
+ DPRINT_EXIT(NETVSC);
+
+ return ret;
+}
+
+static void RndisFilterOnSendCompletion(void *Context)
+{
+ struct rndis_filter_packet *filterPacket = Context;
+
+ DPRINT_ENTER(NETVSC);
+
+ /* Pass it back to the original handler */
+ filterPacket->OnCompletion(filterPacket->CompletionContext);
+
+ DPRINT_EXIT(NETVSC);
+}
+
+
+static void RndisFilterOnSendRequestCompletion(void *Context)
+{
+ DPRINT_ENTER(NETVSC);
+
+ /* Noop */
+ DPRINT_EXIT(NETVSC);
+}
diff --git a/drivers/staging/hv/RndisFilter.h b/drivers/staging/hv/RndisFilter.h
new file mode 100644
index 000000000000..fa7dd79ddebf
--- /dev/null
+++ b/drivers/staging/hv/RndisFilter.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _RNDISFILTER_H_
+#define _RNDISFILTER_H_
+
+#define __struct_bcount(x)
+
+#include "NetVsc.h"
+
+#include "rndis.h"
+
+#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
+
+
+/* Interface */
+
+extern int RndisFilterInit(struct netvsc_driver *driver);
+
+#endif /* _RNDISFILTER_H_ */
diff --git a/drivers/staging/hv/StorVsc.c b/drivers/staging/hv/StorVsc.c
new file mode 100644
index 000000000000..14015c927940
--- /dev/null
+++ b/drivers/staging/hv/StorVsc.c
@@ -0,0 +1,850 @@
+/*
+ * 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/string.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include "osd.h"
+#include "logging.h"
+#include "StorVscApi.h"
+#include "VmbusPacketFormat.h"
+#include "vstorage.h"
+
+
+struct storvsc_request_extension {
+ /* LIST_ENTRY ListEntry; */
+
+ struct hv_storvsc_request *Request;
+ struct hv_device *Device;
+
+ /* Synchronize the request/response if needed */
+ struct osd_waitevent *WaitEvent;
+
+ struct vstor_packet VStorPacket;
+};
+
+/* A storvsc device is a device object that contains a vmbus channel */
+struct storvsc_device {
+ struct hv_device *Device;
+
+ /* 0 indicates the device is being destroyed */
+ atomic_t RefCount;
+
+ atomic_t NumOutstandingRequests;
+
+ /*
+ * Each unique Port/Path/Target represents 1 channel ie scsi
+ * controller. In reality, the pathid, targetid is always 0
+ * and the port is set by us
+ */
+ unsigned int PortNumber;
+ unsigned char PathId;
+ unsigned char TargetId;
+
+ /* LIST_ENTRY OutstandingRequestList; */
+ /* HANDLE OutstandingRequestLock; */
+
+ /* Used for vsc/vsp channel reset process */
+ struct storvsc_request_extension InitRequest;
+ struct storvsc_request_extension ResetRequest;
+};
+
+
+static const char *gDriverName = "storvsc";
+
+/* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
+static const struct hv_guid gStorVscDeviceType = {
+ .data = {
+ 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
+ 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
+ }
+};
+
+
+static inline struct storvsc_device *AllocStorDevice(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+
+ storDevice = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL);
+ if (!storDevice)
+ return NULL;
+
+ /* Set to 2 to allow both inbound and outbound traffics */
+ /* (ie GetStorDevice() and MustGetStorDevice()) to proceed. */
+ atomic_cmpxchg(&storDevice->RefCount, 0, 2);
+
+ storDevice->Device = Device;
+ Device->Extension = storDevice;
+
+ return storDevice;
+}
+
+static inline void FreeStorDevice(struct storvsc_device *Device)
+{
+ ASSERT(atomic_read(&Device->RefCount) == 0);
+ kfree(Device);
+}
+
+/* Get the stordevice object iff exists and its refcount > 1 */
+static inline struct storvsc_device *GetStorDevice(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+
+ storDevice = (struct storvsc_device *)Device->Extension;
+ if (storDevice && atomic_read(&storDevice->RefCount) > 1)
+ atomic_inc(&storDevice->RefCount);
+ else
+ storDevice = NULL;
+
+ return storDevice;
+}
+
+/* Get the stordevice object iff exists and its refcount > 0 */
+static inline struct storvsc_device *MustGetStorDevice(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+
+ storDevice = (struct storvsc_device *)Device->Extension;
+ if (storDevice && atomic_read(&storDevice->RefCount))
+ atomic_inc(&storDevice->RefCount);
+ else
+ storDevice = NULL;
+
+ return storDevice;
+}
+
+static inline void PutStorDevice(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+
+ storDevice = (struct storvsc_device *)Device->Extension;
+ ASSERT(storDevice);
+
+ atomic_dec(&storDevice->RefCount);
+ ASSERT(atomic_read(&storDevice->RefCount));
+}
+
+/* Drop ref count to 1 to effectively disable GetStorDevice() */
+static inline struct storvsc_device *ReleaseStorDevice(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+
+ storDevice = (struct storvsc_device *)Device->Extension;
+ ASSERT(storDevice);
+
+ /* Busy wait until the ref drop to 2, then set it to 1 */
+ while (atomic_cmpxchg(&storDevice->RefCount, 2, 1) != 2)
+ udelay(100);
+
+ return storDevice;
+}
+
+/* Drop ref count to 0. No one can use StorDevice object. */
+static inline struct storvsc_device *FinalReleaseStorDevice(
+ struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+
+ storDevice = (struct storvsc_device *)Device->Extension;
+ ASSERT(storDevice);
+
+ /* Busy wait until the ref drop to 1, then set it to 0 */
+ while (atomic_cmpxchg(&storDevice->RefCount, 1, 0) != 1)
+ udelay(100);
+
+ Device->Extension = NULL;
+ return storDevice;
+}
+
+static int StorVscChannelInit(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+ struct storvsc_request_extension *request;
+ struct vstor_packet *vstorPacket;
+ int ret;
+
+ storDevice = GetStorDevice(Device);
+ if (!storDevice) {
+ DPRINT_ERR(STORVSC, "unable to get stor device..."
+ "device being destroyed?");
+ DPRINT_EXIT(STORVSC);
+ return -1;
+ }
+
+ request = &storDevice->InitRequest;
+ vstorPacket = &request->VStorPacket;
+
+ /*
+ * Now, initiate the vsc/vsp initialization protocol on the open
+ * channel
+ */
+ memset(request, sizeof(struct storvsc_request_extension), 0);
+ request->WaitEvent = osd_WaitEventCreate();
+
+ vstorPacket->Operation = VStorOperationBeginInitialization;
+ vstorPacket->Flags = REQUEST_COMPLETION_FLAG;
+
+ /*SpinlockAcquire(gDriverExt.packetListLock);
+ INSERT_TAIL_LIST(&gDriverExt.packetList, &packet->listEntry.entry);
+ SpinlockRelease(gDriverExt.packetListLock);*/
+
+ DPRINT_INFO(STORVSC, "BEGIN_INITIALIZATION_OPERATION...");
+
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ vstorPacket,
+ sizeof(struct vstor_packet),
+ (unsigned long)request,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC,
+ "unable to send BEGIN_INITIALIZATION_OPERATION");
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(request->WaitEvent);
+
+ if (vstorPacket->Operation != VStorOperationCompleteIo ||
+ vstorPacket->Status != 0) {
+ DPRINT_ERR(STORVSC, "BEGIN_INITIALIZATION_OPERATION failed "
+ "(op %d status 0x%x)",
+ vstorPacket->Operation, vstorPacket->Status);
+ goto Cleanup;
+ }
+
+ DPRINT_INFO(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION...");
+
+ /* reuse the packet for version range supported */
+ memset(vstorPacket, sizeof(struct vstor_packet), 0);
+ vstorPacket->Operation = VStorOperationQueryProtocolVersion;
+ vstorPacket->Flags = REQUEST_COMPLETION_FLAG;
+
+ vstorPacket->Version.MajorMinor = VMSTOR_PROTOCOL_VERSION_CURRENT;
+ FILL_VMSTOR_REVISION(vstorPacket->Version.Revision);
+
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ vstorPacket,
+ sizeof(struct vstor_packet),
+ (unsigned long)request,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC,
+ "unable to send BEGIN_INITIALIZATION_OPERATION");
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(request->WaitEvent);
+
+ /* TODO: Check returned version */
+ if (vstorPacket->Operation != VStorOperationCompleteIo ||
+ vstorPacket->Status != 0) {
+ DPRINT_ERR(STORVSC, "QUERY_PROTOCOL_VERSION_OPERATION failed "
+ "(op %d status 0x%x)",
+ vstorPacket->Operation, vstorPacket->Status);
+ goto Cleanup;
+ }
+
+ /* Query channel properties */
+ DPRINT_INFO(STORVSC, "QUERY_PROPERTIES_OPERATION...");
+
+ memset(vstorPacket, sizeof(struct vstor_packet), 0);
+ vstorPacket->Operation = VStorOperationQueryProperties;
+ vstorPacket->Flags = REQUEST_COMPLETION_FLAG;
+ vstorPacket->StorageChannelProperties.PortNumber =
+ storDevice->PortNumber;
+
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ vstorPacket,
+ sizeof(struct vstor_packet),
+ (unsigned long)request,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC,
+ "unable to send QUERY_PROPERTIES_OPERATION");
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(request->WaitEvent);
+
+ /* TODO: Check returned version */
+ if (vstorPacket->Operation != VStorOperationCompleteIo ||
+ vstorPacket->Status != 0) {
+ DPRINT_ERR(STORVSC, "QUERY_PROPERTIES_OPERATION failed "
+ "(op %d status 0x%x)",
+ vstorPacket->Operation, vstorPacket->Status);
+ goto Cleanup;
+ }
+
+ storDevice->PathId = vstorPacket->StorageChannelProperties.PathId;
+ storDevice->TargetId = vstorPacket->StorageChannelProperties.TargetId;
+
+ DPRINT_DBG(STORVSC, "channel flag 0x%x, max xfer len 0x%x",
+ vstorPacket->StorageChannelProperties.Flags,
+ vstorPacket->StorageChannelProperties.MaxTransferBytes);
+
+ DPRINT_INFO(STORVSC, "END_INITIALIZATION_OPERATION...");
+
+ memset(vstorPacket, sizeof(struct vstor_packet), 0);
+ vstorPacket->Operation = VStorOperationEndInitialization;
+ vstorPacket->Flags = REQUEST_COMPLETION_FLAG;
+
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ vstorPacket,
+ sizeof(struct vstor_packet),
+ (unsigned long)request,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC,
+ "unable to send END_INITIALIZATION_OPERATION");
+ goto Cleanup;
+ }
+
+ osd_WaitEventWait(request->WaitEvent);
+
+ if (vstorPacket->Operation != VStorOperationCompleteIo ||
+ vstorPacket->Status != 0) {
+ DPRINT_ERR(STORVSC, "END_INITIALIZATION_OPERATION failed "
+ "(op %d status 0x%x)",
+ vstorPacket->Operation, vstorPacket->Status);
+ goto Cleanup;
+ }
+
+ DPRINT_INFO(STORVSC, "**** storage channel up and running!! ****");
+
+Cleanup:
+ kfree(request->WaitEvent);
+ request->WaitEvent = NULL;
+
+ PutStorDevice(Device);
+
+ DPRINT_EXIT(STORVSC);
+ return ret;
+}
+
+static void StorVscOnIOCompletion(struct hv_device *Device,
+ struct vstor_packet *VStorPacket,
+ struct storvsc_request_extension *RequestExt)
+{
+ struct hv_storvsc_request *request;
+ struct storvsc_device *storDevice;
+
+ DPRINT_ENTER(STORVSC);
+
+ storDevice = MustGetStorDevice(Device);
+ if (!storDevice) {
+ DPRINT_ERR(STORVSC, "unable to get stor device..."
+ "device being destroyed?");
+ DPRINT_EXIT(STORVSC);
+ return;
+ }
+
+ DPRINT_DBG(STORVSC, "IO_COMPLETE_OPERATION - request extension %p "
+ "completed bytes xfer %u", RequestExt,
+ VStorPacket->VmSrb.DataTransferLength);
+
+ ASSERT(RequestExt != NULL);
+ ASSERT(RequestExt->Request != NULL);
+
+ request = RequestExt->Request;
+
+ ASSERT(request->OnIOCompletion != NULL);
+
+ /* Copy over the status...etc */
+ request->Status = VStorPacket->VmSrb.ScsiStatus;
+
+ if (request->Status != 0 || VStorPacket->VmSrb.SrbStatus != 1) {
+ DPRINT_WARN(STORVSC,
+ "cmd 0x%x scsi status 0x%x srb status 0x%x\n",
+ request->Cdb[0], VStorPacket->VmSrb.ScsiStatus,
+ VStorPacket->VmSrb.SrbStatus);
+ }
+
+ if ((request->Status & 0xFF) == 0x02) {
+ /* CHECK_CONDITION */
+ if (VStorPacket->VmSrb.SrbStatus & 0x80) {
+ /* autosense data available */
+ DPRINT_WARN(STORVSC, "storvsc pkt %p autosense data "
+ "valid - len %d\n", RequestExt,
+ VStorPacket->VmSrb.SenseInfoLength);
+
+ ASSERT(VStorPacket->VmSrb.SenseInfoLength <=
+ request->SenseBufferSize);
+ memcpy(request->SenseBuffer,
+ VStorPacket->VmSrb.SenseData,
+ VStorPacket->VmSrb.SenseInfoLength);
+
+ request->SenseBufferSize =
+ VStorPacket->VmSrb.SenseInfoLength;
+ }
+ }
+
+ /* TODO: */
+ request->BytesXfer = VStorPacket->VmSrb.DataTransferLength;
+
+ request->OnIOCompletion(request);
+
+ atomic_dec(&storDevice->NumOutstandingRequests);
+
+ PutStorDevice(Device);
+
+ DPRINT_EXIT(STORVSC);
+}
+
+static void StorVscOnReceive(struct hv_device *Device,
+ struct vstor_packet *VStorPacket,
+ struct storvsc_request_extension *RequestExt)
+{
+ switch (VStorPacket->Operation) {
+ case VStorOperationCompleteIo:
+ DPRINT_DBG(STORVSC, "IO_COMPLETE_OPERATION");
+ StorVscOnIOCompletion(Device, VStorPacket, RequestExt);
+ break;
+ case VStorOperationRemoveDevice:
+ DPRINT_INFO(STORVSC, "REMOVE_DEVICE_OPERATION");
+ /* TODO: */
+ break;
+
+ default:
+ DPRINT_INFO(STORVSC, "Unknown operation received - %d",
+ VStorPacket->Operation);
+ break;
+ }
+}
+
+static void StorVscOnChannelCallback(void *context)
+{
+ struct hv_device *device = (struct hv_device *)context;
+ struct storvsc_device *storDevice;
+ u32 bytesRecvd;
+ u64 requestId;
+ unsigned char packet[ALIGN_UP(sizeof(struct vstor_packet), 8)];
+ struct storvsc_request_extension *request;
+ int ret;
+
+ DPRINT_ENTER(STORVSC);
+
+ ASSERT(device);
+
+ storDevice = MustGetStorDevice(device);
+ if (!storDevice) {
+ DPRINT_ERR(STORVSC, "unable to get stor device..."
+ "device being destroyed?");
+ DPRINT_EXIT(STORVSC);
+ return;
+ }
+
+ do {
+ ret = device->Driver->VmbusChannelInterface.RecvPacket(device,
+ packet,
+ ALIGN_UP(sizeof(struct vstor_packet), 8),
+ &bytesRecvd, &requestId);
+ if (ret == 0 && bytesRecvd > 0) {
+ DPRINT_DBG(STORVSC, "receive %d bytes - tid %llx",
+ bytesRecvd, requestId);
+
+ /* ASSERT(bytesRecvd == sizeof(struct vstor_packet)); */
+
+ request = (struct storvsc_request_extension *)
+ (unsigned long)requestId;
+ ASSERT(request);
+
+ /* if (vstorPacket.Flags & SYNTHETIC_FLAG) */
+ if ((request == &storDevice->InitRequest) ||
+ (request == &storDevice->ResetRequest)) {
+ /* DPRINT_INFO(STORVSC,
+ * "reset completion - operation "
+ * "%u status %u",
+ * vstorPacket.Operation,
+ * vstorPacket.Status); */
+
+ memcpy(&request->VStorPacket, packet,
+ sizeof(struct vstor_packet));
+
+ osd_WaitEventSet(request->WaitEvent);
+ } else {
+ StorVscOnReceive(device,
+ (struct vstor_packet *)packet,
+ request);
+ }
+ } else {
+ /* DPRINT_DBG(STORVSC, "nothing else to read..."); */
+ break;
+ }
+ } while (1);
+
+ PutStorDevice(device);
+
+ DPRINT_EXIT(STORVSC);
+ return;
+}
+
+static int StorVscConnectToVsp(struct hv_device *Device)
+{
+ struct vmstorage_channel_properties props;
+ struct storvsc_driver_object *storDriver;
+ int ret;
+
+ storDriver = (struct storvsc_driver_object *)Device->Driver;
+ memset(&props, sizeof(struct vmstorage_channel_properties), 0);
+
+ /* Open the channel */
+ ret = Device->Driver->VmbusChannelInterface.Open(Device,
+ storDriver->RingBufferSize,
+ storDriver->RingBufferSize,
+ (void *)&props,
+ sizeof(struct vmstorage_channel_properties),
+ StorVscOnChannelCallback,
+ Device);
+
+ DPRINT_DBG(STORVSC, "storage props: path id %d, tgt id %d, max xfer %d",
+ props.PathId, props.TargetId, props.MaxTransferBytes);
+
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC, "unable to open channel: %d", ret);
+ return -1;
+ }
+
+ ret = StorVscChannelInit(Device);
+
+ return ret;
+}
+
+/**
+ * StorVscOnDeviceAdd - Callback when the device belonging to this driver is added
+ */
+static int StorVscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo)
+{
+ struct storvsc_device *storDevice;
+ /* struct vmstorage_channel_properties *props; */
+ struct storvsc_device_info *deviceInfo;
+ int ret = 0;
+
+ DPRINT_ENTER(STORVSC);
+
+ deviceInfo = (struct storvsc_device_info *)AdditionalInfo;
+ storDevice = AllocStorDevice(Device);
+ if (!storDevice) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ /* Save the channel properties to our storvsc channel */
+ /* props = (struct vmstorage_channel_properties *)
+ * channel->offerMsg.Offer.u.Standard.UserDefined; */
+
+ /* FIXME: */
+ /*
+ * If we support more than 1 scsi channel, we need to set the
+ * port number here to the scsi channel but how do we get the
+ * scsi channel prior to the bus scan
+ */
+
+ /* storChannel->PortNumber = 0;
+ storChannel->PathId = props->PathId;
+ storChannel->TargetId = props->TargetId; */
+
+ storDevice->PortNumber = deviceInfo->PortNumber;
+ /* Send it back up */
+ ret = StorVscConnectToVsp(Device);
+
+ /* deviceInfo->PortNumber = storDevice->PortNumber; */
+ deviceInfo->PathId = storDevice->PathId;
+ deviceInfo->TargetId = storDevice->TargetId;
+
+ DPRINT_DBG(STORVSC, "assigned port %u, path %u target %u\n",
+ storDevice->PortNumber, storDevice->PathId,
+ storDevice->TargetId);
+
+Cleanup:
+ DPRINT_EXIT(STORVSC);
+
+ return ret;
+}
+
+/**
+ * StorVscOnDeviceRemove - Callback when the our device is being removed
+ */
+static int StorVscOnDeviceRemove(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+
+ DPRINT_ENTER(STORVSC);
+
+ DPRINT_INFO(STORVSC, "disabling storage device (%p)...",
+ Device->Extension);
+
+ storDevice = ReleaseStorDevice(Device);
+
+ /*
+ * At this point, all outbound traffic should be disable. We
+ * only allow inbound traffic (responses) to proceed so that
+ * outstanding requests can be completed.
+ */
+ while (atomic_read(&storDevice->NumOutstandingRequests)) {
+ DPRINT_INFO(STORVSC, "waiting for %d requests to complete...",
+ atomic_read(&storDevice->NumOutstandingRequests));
+ udelay(100);
+ }
+
+ DPRINT_INFO(STORVSC, "removing storage device (%p)...",
+ Device->Extension);
+
+ storDevice = FinalReleaseStorDevice(Device);
+
+ DPRINT_INFO(STORVSC, "storage device (%p) safe to remove", storDevice);
+
+ /* Close the channel */
+ Device->Driver->VmbusChannelInterface.Close(Device);
+
+ FreeStorDevice(storDevice);
+
+ DPRINT_EXIT(STORVSC);
+ return 0;
+}
+
+static int StorVscOnHostReset(struct hv_device *Device)
+{
+ struct storvsc_device *storDevice;
+ struct storvsc_request_extension *request;
+ struct vstor_packet *vstorPacket;
+ int ret;
+
+ DPRINT_ENTER(STORVSC);
+
+ DPRINT_INFO(STORVSC, "resetting host adapter...");
+
+ storDevice = GetStorDevice(Device);
+ if (!storDevice) {
+ DPRINT_ERR(STORVSC, "unable to get stor device..."
+ "device being destroyed?");
+ DPRINT_EXIT(STORVSC);
+ return -1;
+ }
+
+ request = &storDevice->ResetRequest;
+ vstorPacket = &request->VStorPacket;
+
+ request->WaitEvent = osd_WaitEventCreate();
+
+ vstorPacket->Operation = VStorOperationResetBus;
+ vstorPacket->Flags = REQUEST_COMPLETION_FLAG;
+ vstorPacket->VmSrb.PathId = storDevice->PathId;
+
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ vstorPacket,
+ sizeof(struct vstor_packet),
+ (unsigned long)&storDevice->ResetRequest,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC, "Unable to send reset packet %p ret %d",
+ vstorPacket, ret);
+ goto Cleanup;
+ }
+
+ /* FIXME: Add a timeout */
+ osd_WaitEventWait(request->WaitEvent);
+
+ kfree(request->WaitEvent);
+ DPRINT_INFO(STORVSC, "host adapter reset completed");
+
+ /*
+ * At this point, all outstanding requests in the adapter
+ * should have been flushed out and return to us
+ */
+
+Cleanup:
+ PutStorDevice(Device);
+ DPRINT_EXIT(STORVSC);
+ return ret;
+}
+
+/**
+ * StorVscOnIORequest - Callback to initiate an I/O request
+ */
+static int StorVscOnIORequest(struct hv_device *Device,
+ struct hv_storvsc_request *Request)
+{
+ struct storvsc_device *storDevice;
+ struct storvsc_request_extension *requestExtension;
+ struct vstor_packet *vstorPacket;
+ int ret = 0;
+
+ DPRINT_ENTER(STORVSC);
+
+ requestExtension =
+ (struct storvsc_request_extension *)Request->Extension;
+ vstorPacket = &requestExtension->VStorPacket;
+ storDevice = GetStorDevice(Device);
+
+ DPRINT_DBG(STORVSC, "enter - Device %p, DeviceExt %p, Request %p, "
+ "Extension %p", Device, storDevice, Request,
+ requestExtension);
+
+ DPRINT_DBG(STORVSC, "req %p len %d bus %d, target %d, lun %d cdblen %d",
+ Request, Request->DataBuffer.Length, Request->Bus,
+ Request->TargetId, Request->LunId, Request->CdbLen);
+
+ if (!storDevice) {
+ DPRINT_ERR(STORVSC, "unable to get stor device..."
+ "device being destroyed?");
+ DPRINT_EXIT(STORVSC);
+ return -2;
+ }
+
+ /* print_hex_dump_bytes("", DUMP_PREFIX_NONE, Request->Cdb,
+ * Request->CdbLen); */
+
+ requestExtension->Request = Request;
+ requestExtension->Device = Device;
+
+ memset(vstorPacket, 0 , sizeof(struct vstor_packet));
+
+ vstorPacket->Flags |= REQUEST_COMPLETION_FLAG;
+
+ vstorPacket->VmSrb.Length = sizeof(struct vmscsi_request);
+
+ vstorPacket->VmSrb.PortNumber = Request->Host;
+ vstorPacket->VmSrb.PathId = Request->Bus;
+ vstorPacket->VmSrb.TargetId = Request->TargetId;
+ vstorPacket->VmSrb.Lun = Request->LunId;
+
+ vstorPacket->VmSrb.SenseInfoLength = SENSE_BUFFER_SIZE;
+
+ /* Copy over the scsi command descriptor block */
+ vstorPacket->VmSrb.CdbLength = Request->CdbLen;
+ memcpy(&vstorPacket->VmSrb.Cdb, Request->Cdb, Request->CdbLen);
+
+ vstorPacket->VmSrb.DataIn = Request->Type;
+ vstorPacket->VmSrb.DataTransferLength = Request->DataBuffer.Length;
+
+ vstorPacket->Operation = VStorOperationExecuteSRB;
+
+ DPRINT_DBG(STORVSC, "srb - len %d port %d, path %d, target %d, "
+ "lun %d senselen %d cdblen %d",
+ vstorPacket->VmSrb.Length,
+ vstorPacket->VmSrb.PortNumber,
+ vstorPacket->VmSrb.PathId,
+ vstorPacket->VmSrb.TargetId,
+ vstorPacket->VmSrb.Lun,
+ vstorPacket->VmSrb.SenseInfoLength,
+ vstorPacket->VmSrb.CdbLength);
+
+ if (requestExtension->Request->DataBuffer.Length) {
+ ret = Device->Driver->VmbusChannelInterface.
+ SendPacketMultiPageBuffer(Device,
+ &requestExtension->Request->DataBuffer,
+ vstorPacket,
+ sizeof(struct vstor_packet),
+ (unsigned long)requestExtension);
+ } else {
+ ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+ vstorPacket,
+ sizeof(struct vstor_packet),
+ (unsigned long)requestExtension,
+ VmbusPacketTypeDataInBand,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ }
+
+ if (ret != 0) {
+ DPRINT_DBG(STORVSC, "Unable to send packet %p ret %d",
+ vstorPacket, ret);
+ }
+
+ atomic_inc(&storDevice->NumOutstandingRequests);
+
+ PutStorDevice(Device);
+
+ DPRINT_EXIT(STORVSC);
+ return ret;
+}
+
+/**
+ * StorVscOnCleanup - Perform any cleanup when the driver is removed
+ */
+static void StorVscOnCleanup(struct hv_driver *Driver)
+{
+ DPRINT_ENTER(STORVSC);
+ DPRINT_EXIT(STORVSC);
+}
+
+/**
+ * StorVscInitialize - Main entry point
+ */
+int StorVscInitialize(struct hv_driver *Driver)
+{
+ struct storvsc_driver_object *storDriver;
+
+ DPRINT_ENTER(STORVSC);
+
+ storDriver = (struct storvsc_driver_object *)Driver;
+
+ DPRINT_DBG(STORVSC, "sizeof(STORVSC_REQUEST)=%zd "
+ "sizeof(struct storvsc_request_extension)=%zd "
+ "sizeof(struct vstor_packet)=%zd, "
+ "sizeof(struct vmscsi_request)=%zd",
+ sizeof(struct hv_storvsc_request),
+ sizeof(struct storvsc_request_extension),
+ sizeof(struct vstor_packet),
+ sizeof(struct vmscsi_request));
+
+ /* Make sure we are at least 2 pages since 1 page is used for control */
+ ASSERT(storDriver->RingBufferSize >= (PAGE_SIZE << 1));
+
+ Driver->name = gDriverName;
+ memcpy(&Driver->deviceType, &gStorVscDeviceType,
+ sizeof(struct hv_guid));
+
+ storDriver->RequestExtSize = sizeof(struct storvsc_request_extension);
+
+ /*
+ * Divide the ring buffer data size (which is 1 page less
+ * than the ring buffer size since that page is reserved for
+ * the ring buffer indices) by the max request size (which is
+ * VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER + struct vstor_packet + u64)
+ */
+ storDriver->MaxOutstandingRequestsPerChannel =
+ ((storDriver->RingBufferSize - PAGE_SIZE) /
+ ALIGN_UP(MAX_MULTIPAGE_BUFFER_PACKET +
+ sizeof(struct vstor_packet) + sizeof(u64),
+ sizeof(u64)));
+
+ DPRINT_INFO(STORVSC, "max io %u, currently %u\n",
+ storDriver->MaxOutstandingRequestsPerChannel,
+ STORVSC_MAX_IO_REQUESTS);
+
+ /* Setup the dispatch table */
+ storDriver->Base.OnDeviceAdd = StorVscOnDeviceAdd;
+ storDriver->Base.OnDeviceRemove = StorVscOnDeviceRemove;
+ storDriver->Base.OnCleanup = StorVscOnCleanup;
+
+ storDriver->OnIORequest = StorVscOnIORequest;
+ storDriver->OnHostReset = StorVscOnHostReset;
+
+ DPRINT_EXIT(STORVSC);
+
+ return 0;
+}
diff --git a/drivers/staging/hv/StorVscApi.h b/drivers/staging/hv/StorVscApi.h
new file mode 100644
index 000000000000..69c14066c479
--- /dev/null
+++ b/drivers/staging/hv/StorVscApi.h
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _STORVSC_API_H_
+#define _STORVSC_API_H_
+
+#include "VmbusApi.h"
+
+/* Defines */
+#define STORVSC_RING_BUFFER_SIZE (10*PAGE_SIZE)
+#define BLKVSC_RING_BUFFER_SIZE (20*PAGE_SIZE)
+
+#define STORVSC_MAX_IO_REQUESTS 64
+
+/*
+ * In Hyper-V, each port/path/target maps to 1 scsi host adapter. In
+ * reality, the path/target is not used (ie always set to 0) so our
+ * scsi host adapter essentially has 1 bus with 1 target that contains
+ * up to 256 luns.
+ */
+#define STORVSC_MAX_LUNS_PER_TARGET 64
+#define STORVSC_MAX_TARGETS 1
+#define STORVSC_MAX_CHANNELS 1
+
+struct hv_storvsc_request;
+
+/* Matches Windows-end */
+enum storvsc_request_type{
+ WRITE_TYPE,
+ READ_TYPE,
+ UNKNOWN_TYPE,
+};
+
+struct hv_storvsc_request {
+ enum storvsc_request_type Type;
+ u32 Host;
+ u32 Bus;
+ u32 TargetId;
+ u32 LunId;
+ u8 *Cdb;
+ u32 CdbLen;
+ u32 Status;
+ u32 BytesXfer;
+
+ unsigned char *SenseBuffer;
+ u32 SenseBufferSize;
+
+ void *Context;
+
+ void (*OnIOCompletion)(struct hv_storvsc_request *Request);
+
+ /* This points to the memory after DataBuffer */
+ void *Extension;
+
+ struct hv_multipage_buffer DataBuffer;
+};
+
+/* Represents the block vsc driver */
+struct storvsc_driver_object {
+ /* Must be the first field */
+ /* Which is a bug FIXME! */
+ struct hv_driver Base;
+
+ /* Set by caller (in bytes) */
+ u32 RingBufferSize;
+
+ /* Allocate this much private extension for each I/O request */
+ u32 RequestExtSize;
+
+ /* Maximum # of requests in flight per channel/device */
+ u32 MaxOutstandingRequestsPerChannel;
+
+ /* Set by the caller to allow us to re-enumerate the bus on the host */
+ void (*OnHostRescan)(struct hv_device *Device);
+
+ /* Specific to this driver */
+ int (*OnIORequest)(struct hv_device *Device,
+ struct hv_storvsc_request *Request);
+ int (*OnHostReset)(struct hv_device *Device);
+};
+
+struct storvsc_device_info {
+ unsigned int PortNumber;
+ unsigned char PathId;
+ unsigned char TargetId;
+};
+
+/* Interface */
+int StorVscInitialize(struct hv_driver *driver);
+int BlkVscInitialize(struct hv_driver *driver);
+
+#endif /* _STORVSC_API_H_ */
diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO
new file mode 100644
index 000000000000..4d390b237742
--- /dev/null
+++ b/drivers/staging/hv/TODO
@@ -0,0 +1,13 @@
+TODO:
+ - fix remaining checkpatch warnings and errors
+ - remove RingBuffer.c to us in-kernel ringbuffer functions instead.
+ - audit the vmbus to verify it is working properly with the
+ driver model
+ - see if the vmbus can be merged with the other virtual busses
+ in the kernel
+ - audit the network driver
+ - audit the block driver
+ - audit the scsi driver
+
+Please send patches for this code to Greg Kroah-Hartman <gregkh@suse.de>,
+Hank Janssen <hjanssen@microsoft.com>, and Haiyang Zhang <haiyangz@microsoft.com>.
diff --git a/drivers/staging/hv/VersionInfo.h b/drivers/staging/hv/VersionInfo.h
new file mode 100644
index 000000000000..9c3641d99ed8
--- /dev/null
+++ b/drivers/staging/hv/VersionInfo.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+#ifndef __HV_VERSION_INFO
+#define __HV_VERSION_INFO
+
+static const char VersionDate[] = __DATE__;
+static const char VersionTime[] = __TIME__;
+static const char VersionDesc[] = "Version 2.0";
+
+#endif
diff --git a/drivers/staging/hv/Vmbus.c b/drivers/staging/hv/Vmbus.c
new file mode 100644
index 000000000000..a4dd06f6d459
--- /dev/null
+++ b/drivers/staging/hv/Vmbus.c
@@ -0,0 +1,311 @@
+/*
+ * 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/mm.h>
+#include "osd.h"
+#include "logging.h"
+#include "VersionInfo.h"
+#include "VmbusPrivate.h"
+
+static const char *gDriverName = "vmbus";
+
+/*
+ * Windows vmbus does not defined this.
+ * We defined this to be consistent with other devices
+ */
+/* {c5295816-f63a-4d5f-8d1a-4daf999ca185} */
+static const struct hv_guid gVmbusDeviceType = {
+ .data = {
+ 0x16, 0x58, 0x29, 0xc5, 0x3a, 0xf6, 0x5f, 0x4d,
+ 0x8d, 0x1a, 0x4d, 0xaf, 0x99, 0x9c, 0xa1, 0x85
+ }
+};
+
+/* {ac3760fc-9adf-40aa-9427-a70ed6de95c5} */
+static const struct hv_guid gVmbusDeviceId = {
+ .data = {
+ 0xfc, 0x60, 0x37, 0xac, 0xdf, 0x9a, 0xaa, 0x40,
+ 0x94, 0x27, 0xa7, 0x0e, 0xd6, 0xde, 0x95, 0xc5
+ }
+};
+
+static struct hv_driver *gDriver; /* vmbus driver object */
+static struct hv_device *gDevice; /* vmbus root device */
+
+/**
+ * VmbusGetChannelOffers - Retrieve the channel offers from the parent partition
+ */
+static void VmbusGetChannelOffers(void)
+{
+ DPRINT_ENTER(VMBUS);
+ VmbusChannelRequestOffers();
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusGetChannelInterface - Get the channel interface
+ */
+static void VmbusGetChannelInterface(struct vmbus_channel_interface *Interface)
+{
+ GetChannelInterface(Interface);
+}
+
+/**
+ * VmbusGetChannelInfo - Get the device info for the specified device object
+ */
+static void VmbusGetChannelInfo(struct hv_device *DeviceObject,
+ struct hv_device_info *DeviceInfo)
+{
+ GetChannelInfo(DeviceObject, DeviceInfo);
+}
+
+/**
+ * VmbusCreateChildDevice - Creates the child device on the bus that represents the channel offer
+ */
+struct hv_device *VmbusChildDeviceCreate(struct hv_guid *DeviceType,
+ struct hv_guid *DeviceInstance,
+ void *Context)
+{
+ struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver;
+
+ return vmbusDriver->OnChildDeviceCreate(DeviceType, DeviceInstance,
+ Context);
+}
+
+/**
+ * VmbusChildDeviceAdd - Registers the child device with the vmbus
+ */
+int VmbusChildDeviceAdd(struct hv_device *ChildDevice)
+{
+ struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver;
+
+ return vmbusDriver->OnChildDeviceAdd(gDevice, ChildDevice);
+}
+
+/**
+ * VmbusChildDeviceRemove Unregisters the child device from the vmbus
+ */
+void VmbusChildDeviceRemove(struct hv_device *ChildDevice)
+{
+ struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver;
+
+ vmbusDriver->OnChildDeviceRemove(ChildDevice);
+}
+
+/**
+ * VmbusOnDeviceAdd - Callback when the root bus device is added
+ */
+static int VmbusOnDeviceAdd(struct hv_device *dev, void *AdditionalInfo)
+{
+ u32 *irqvector = AdditionalInfo;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+
+ gDevice = dev;
+
+ memcpy(&gDevice->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid));
+ memcpy(&gDevice->deviceInstance, &gVmbusDeviceId,
+ sizeof(struct hv_guid));
+
+ /* strcpy(dev->name, "vmbus"); */
+ /* SynIC setup... */
+ ret = HvSynicInit(*irqvector);
+
+ /* Connect to VMBus in the root partition */
+ ret = VmbusConnect();
+
+ /* VmbusSendEvent(device->localPortId+1); */
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusOnDeviceRemove - Callback when the root bus device is removed
+ */
+static int VmbusOnDeviceRemove(struct hv_device *dev)
+{
+ int ret = 0;
+
+ DPRINT_ENTER(VMBUS);
+ VmbusChannelReleaseUnattachedChannels();
+ VmbusDisconnect();
+ HvSynicCleanup();
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
+
+/**
+ * VmbusOnCleanup - Perform any cleanup when the driver is removed
+ */
+static void VmbusOnCleanup(struct hv_driver *drv)
+{
+ /* struct vmbus_driver *driver = (struct vmbus_driver *)drv; */
+
+ DPRINT_ENTER(VMBUS);
+ HvCleanup();
+ DPRINT_EXIT(VMBUS);
+}
+
+/**
+ * VmbusOnMsgDPC - DPC routine to handle messages from the hypervisior
+ */
+static void VmbusOnMsgDPC(struct hv_driver *drv)
+{
+ void *page_addr = gHvContext.synICMessagePage[0];
+ struct hv_message *msg = (struct hv_message *)page_addr +
+ VMBUS_MESSAGE_SINT;
+ struct hv_message *copied;
+
+ while (1) {
+ if (msg->Header.MessageType == HvMessageTypeNone) {
+ /* no msg */
+ break;
+ } else {
+ copied = kmalloc(sizeof(*copied), GFP_ATOMIC);
+ if (copied == NULL)
+ continue;
+
+ memcpy(copied, msg, sizeof(*copied));
+ osd_schedule_callback(gVmbusConnection.WorkQueue,
+ VmbusOnChannelMessage,
+ (void *)copied);
+ }
+
+ msg->Header.MessageType = HvMessageTypeNone;
+
+ /*
+ * Make sure the write to MessageType (ie set to
+ * HvMessageTypeNone) happens before we read the
+ * MessagePending and EOMing. Otherwise, the EOMing
+ * will not deliver any more messages since there is
+ * no empty slot
+ */
+ mb();
+
+ if (msg->Header.MessageFlags.MessagePending) {
+ /*
+ * This will cause message queue rescan to
+ * possibly deliver another msg from the
+ * hypervisor
+ */
+ wrmsrl(HV_X64_MSR_EOM, 0);
+ }
+ }
+}
+
+/**
+ * VmbusOnEventDPC - DPC routine to handle events from the hypervisior
+ */
+static void VmbusOnEventDPC(struct hv_driver *drv)
+{
+ /* TODO: Process any events */
+ VmbusOnEvents();
+}
+
+/**
+ * VmbusOnISR - ISR routine
+ */
+static int VmbusOnISR(struct hv_driver *drv)
+{
+ int ret = 0;
+ void *page_addr;
+ struct hv_message *msg;
+ union hv_synic_event_flags *event;
+
+ page_addr = gHvContext.synICMessagePage[0];
+ msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
+
+ DPRINT_ENTER(VMBUS);
+
+ /* Check if there are actual msgs to be process */
+ if (msg->Header.MessageType != HvMessageTypeNone) {
+ DPRINT_DBG(VMBUS, "received msg type %d size %d",
+ msg->Header.MessageType,
+ msg->Header.PayloadSize);
+ ret |= 0x1;
+ }
+
+ /* TODO: Check if there are events to be process */
+ page_addr = gHvContext.synICEventPage[0];
+ event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT;
+
+ /* Since we are a child, we only need to check bit 0 */
+ if (test_and_clear_bit(0, (unsigned long *) &event->Flags32[0])) {
+ DPRINT_DBG(VMBUS, "received event %d", event->Flags32[0]);
+ ret |= 0x2;
+ }
+
+ DPRINT_EXIT(VMBUS);
+ return ret;
+}
+
+/**
+ * VmbusInitialize - Main entry point
+ */
+int VmbusInitialize(struct hv_driver *drv)
+{
+ struct vmbus_driver *driver = (struct vmbus_driver *)drv;
+ int ret;
+
+ DPRINT_ENTER(VMBUS);
+
+ DPRINT_INFO(VMBUS, "+++++++ Build Date=%s %s +++++++",
+ VersionDate, VersionTime);
+ DPRINT_INFO(VMBUS, "+++++++ Build Description=%s +++++++",
+ VersionDesc);
+ DPRINT_INFO(VMBUS, "+++++++ Vmbus supported version = %d +++++++",
+ VMBUS_REVISION_NUMBER);
+ DPRINT_INFO(VMBUS, "+++++++ Vmbus using SINT %d +++++++",
+ VMBUS_MESSAGE_SINT);
+ DPRINT_DBG(VMBUS, "sizeof(VMBUS_CHANNEL_PACKET_PAGE_BUFFER)=%zd, "
+ "sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)=%zd",
+ sizeof(struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER),
+ sizeof(struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER));
+
+ drv->name = gDriverName;
+ memcpy(&drv->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid));
+
+ /* Setup dispatch table */
+ driver->Base.OnDeviceAdd = VmbusOnDeviceAdd;
+ driver->Base.OnDeviceRemove = VmbusOnDeviceRemove;
+ driver->Base.OnCleanup = VmbusOnCleanup;
+ driver->OnIsr = VmbusOnISR;
+ driver->OnMsgDpc = VmbusOnMsgDPC;
+ driver->OnEventDpc = VmbusOnEventDPC;
+ driver->GetChannelOffers = VmbusGetChannelOffers;
+ driver->GetChannelInterface = VmbusGetChannelInterface;
+ driver->GetChannelInfo = VmbusGetChannelInfo;
+
+ /* Hypervisor initialization...setup hypercall page..etc */
+ ret = HvInit();
+ if (ret != 0)
+ DPRINT_ERR(VMBUS, "Unable to initialize the hypervisor - 0x%x",
+ ret);
+ gDriver = drv;
+
+ DPRINT_EXIT(VMBUS);
+
+ return ret;
+}
diff --git a/drivers/staging/hv/VmbusApi.h b/drivers/staging/hv/VmbusApi.h
new file mode 100644
index 000000000000..d089bb193e7d
--- /dev/null
+++ b/drivers/staging/hv/VmbusApi.h
@@ -0,0 +1,175 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _VMBUS_API_H_
+#define _VMBUS_API_H_
+
+#define MAX_PAGE_BUFFER_COUNT 16
+#define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */
+
+#pragma pack(push, 1)
+
+/* Single-page buffer */
+struct hv_page_buffer {
+ u32 Length;
+ u32 Offset;
+ u64 Pfn;
+};
+
+/* Multiple-page buffer */
+struct hv_multipage_buffer {
+ /* Length and Offset determines the # of pfns in the array */
+ u32 Length;
+ u32 Offset;
+ u64 PfnArray[MAX_MULTIPAGE_BUFFER_COUNT];
+};
+
+/* 0x18 includes the proprietary packet header */
+#define MAX_PAGE_BUFFER_PACKET (0x18 + \
+ (sizeof(struct hv_page_buffer) * \
+ MAX_PAGE_BUFFER_COUNT))
+#define MAX_MULTIPAGE_BUFFER_PACKET (0x18 + \
+ sizeof(struct hv_multipage_buffer))
+
+
+#pragma pack(pop)
+
+struct hv_driver;
+struct hv_device;
+
+struct hv_dev_port_info {
+ u32 InterruptMask;
+ u32 ReadIndex;
+ u32 WriteIndex;
+ u32 BytesAvailToRead;
+ u32 BytesAvailToWrite;
+};
+
+struct hv_device_info {
+ u32 ChannelId;
+ u32 ChannelState;
+ struct hv_guid ChannelType;
+ struct hv_guid ChannelInstance;
+
+ u32 MonitorId;
+ u32 ServerMonitorPending;
+ u32 ServerMonitorLatency;
+ u32 ServerMonitorConnectionId;
+ u32 ClientMonitorPending;
+ u32 ClientMonitorLatency;
+ u32 ClientMonitorConnectionId;
+
+ struct hv_dev_port_info Inbound;
+ struct hv_dev_port_info Outbound;
+};
+
+struct vmbus_channel_interface {
+ int (*Open)(struct hv_device *Device, u32 SendBufferSize,
+ u32 RecvRingBufferSize, void *UserData, u32 UserDataLen,
+ void (*ChannelCallback)(void *context),
+ void *Context);
+ void (*Close)(struct hv_device *device);
+ int (*SendPacket)(struct hv_device *Device, const void *Buffer,
+ u32 BufferLen, u64 RequestId, u32 Type, u32 Flags);
+ int (*SendPacketPageBuffer)(struct hv_device *dev,
+ struct hv_page_buffer PageBuffers[],
+ u32 PageCount, void *Buffer, u32 BufferLen,
+ u64 RequestId);
+ int (*SendPacketMultiPageBuffer)(struct hv_device *device,
+ struct hv_multipage_buffer *mpb,
+ void *Buffer,
+ u32 BufferLen,
+ u64 RequestId);
+ int (*RecvPacket)(struct hv_device *dev, void *buf, u32 buflen,
+ u32 *BufferActualLen, u64 *RequestId);
+ int (*RecvPacketRaw)(struct hv_device *dev, void *buf, u32 buflen,
+ u32 *BufferActualLen, u64 *RequestId);
+ int (*EstablishGpadl)(struct hv_device *dev, void *buf, u32 buflen,
+ u32 *GpadlHandle);
+ int (*TeardownGpadl)(struct hv_device *device, u32 GpadlHandle);
+ void (*GetInfo)(struct hv_device *dev, struct hv_device_info *devinfo);
+};
+
+/* Base driver object */
+struct hv_driver {
+ const char *name;
+
+ /* the device type supported by this driver */
+ struct hv_guid deviceType;
+
+ int (*OnDeviceAdd)(struct hv_device *device, void *data);
+ int (*OnDeviceRemove)(struct hv_device *device);
+ void (*OnCleanup)(struct hv_driver *driver);
+
+ struct vmbus_channel_interface VmbusChannelInterface;
+};
+
+/* Base device object */
+struct hv_device {
+ /* the driver for this device */
+ struct hv_driver *Driver;
+
+ char name[64];
+
+ /* the device type id of this device */
+ struct hv_guid deviceType;
+
+ /* the device instance id of this device */
+ struct hv_guid deviceInstance;
+
+ void *context;
+
+ /* Device extension; */
+ void *Extension;
+};
+
+/* Vmbus driver object */
+struct vmbus_driver {
+ /* !! Must be the 1st field !! */
+ /* FIXME if ^, then someone is doing somthing stupid */
+ struct hv_driver Base;
+
+ /* Set by the caller */
+ struct hv_device * (*OnChildDeviceCreate)(struct hv_guid *DeviceType,
+ struct hv_guid *DeviceInstance,
+ void *Context);
+ void (*OnChildDeviceDestroy)(struct hv_device *device);
+ int (*OnChildDeviceAdd)(struct hv_device *RootDevice,
+ struct hv_device *ChildDevice);
+ void (*OnChildDeviceRemove)(struct hv_device *device);
+
+ /* Set by the callee */
+ int (*OnIsr)(struct hv_driver *driver);
+ void (*OnMsgDpc)(struct hv_driver *driver);
+ void (*OnEventDpc)(struct hv_driver *driver);
+ void (*GetChannelOffers)(void);
+
+ void (*GetChannelInterface)(struct vmbus_channel_interface *i);
+ void (*GetChannelInfo)(struct hv_device *dev,
+ struct hv_device_info *devinfo);
+};
+
+int VmbusInitialize(struct hv_driver *drv);
+
+#endif /* _VMBUS_API_H_ */
diff --git a/drivers/staging/hv/VmbusChannelInterface.h b/drivers/staging/hv/VmbusChannelInterface.h
new file mode 100644
index 000000000000..26742823748d
--- /dev/null
+++ b/drivers/staging/hv/VmbusChannelInterface.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+#ifndef __VMBUSCHANNELINTERFACE_H
+#define __VMBUSCHANNELINTERFACE_H
+
+/*
+ * A revision number of vmbus that is used for ensuring both ends on a
+ * partition are using compatible versions.
+ */
+#define VMBUS_REVISION_NUMBER 13
+
+/* Make maximum size of pipe payload of 16K */
+#define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384)
+
+/* Define PipeMode values. */
+#define VMBUS_PIPE_TYPE_BYTE 0x00000000
+#define VMBUS_PIPE_TYPE_MESSAGE 0x00000004
+
+/* The size of the user defined data buffer for non-pipe offers. */
+#define MAX_USER_DEFINED_BYTES 120
+
+/* The size of the user defined data buffer for pipe offers. */
+#define MAX_PIPE_USER_DEFINED_BYTES 116
+
+/*
+ * At the center of the Channel Management library is the Channel Offer. This
+ * struct contains the fundamental information about an offer.
+ */
+struct vmbus_channel_offer {
+ struct hv_guid InterfaceType;
+ struct hv_guid InterfaceInstance;
+ u64 InterruptLatencyIn100nsUnits;
+ u32 InterfaceRevision;
+ u32 ServerContextAreaSize; /* in bytes */
+ u16 ChannelFlags;
+ u16 MmioMegabytes; /* in bytes * 1024 * 1024 */
+
+ union {
+ /* Non-pipes: The user has MAX_USER_DEFINED_BYTES bytes. */
+ struct {
+ unsigned char UserDefined[MAX_USER_DEFINED_BYTES];
+ } Standard;
+
+ /*
+ * Pipes:
+ * The following sructure is an integrated pipe protocol, which
+ * is implemented on top of standard user-defined data. Pipe
+ * clients have MAX_PIPE_USER_DEFINED_BYTES left for their own
+ * use.
+ */
+ struct {
+ u32 PipeMode;
+ unsigned char UserDefined[MAX_PIPE_USER_DEFINED_BYTES];
+ } Pipe;
+ } u;
+ u32 Padding;
+} __attribute__((packed));
+
+/* Server Flags */
+#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 1
+#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 2
+#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 4
+#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10
+#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100
+#define VMBUS_CHANNEL_PARENT_OFFER 0x200
+#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400
+
+#endif
diff --git a/drivers/staging/hv/VmbusPacketFormat.h b/drivers/staging/hv/VmbusPacketFormat.h
new file mode 100644
index 000000000000..79120bc742dc
--- /dev/null
+++ b/drivers/staging/hv/VmbusPacketFormat.h
@@ -0,0 +1,160 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+#ifndef _VMBUSPACKETFORMAT_H_
+
+struct vmpacket_descriptor {
+ u16 Type;
+ u16 DataOffset8;
+ u16 Length8;
+ u16 Flags;
+ u64 TransactionId;
+} __attribute__((packed));
+
+struct vmpacket_header {
+ u32 PreviousPacketStartOffset;
+ struct vmpacket_descriptor Descriptor;
+} __attribute__((packed));
+
+struct vmtransfer_page_range {
+ u32 ByteCount;
+ u32 ByteOffset;
+} __attribute__((packed));
+
+struct vmtransfer_page_packet_header {
+ struct vmpacket_descriptor d;
+ u16 TransferPageSetId;
+ bool SenderOwnsSet;
+ u8 Reserved;
+ u32 RangeCount;
+ struct vmtransfer_page_range Ranges[1];
+} __attribute__((packed));
+
+struct vmgpadl_packet_header {
+ struct vmpacket_descriptor d;
+ u32 Gpadl;
+ u32 Reserved;
+} __attribute__((packed));
+
+struct vmadd_remove_transfer_page_set {
+ struct vmpacket_descriptor d;
+ u32 Gpadl;
+ u16 TransferPageSetId;
+ u16 Reserved;
+} __attribute__((packed));
+
+/*
+ * This structure defines a range in guest physical space that can be made to
+ * look virtually contiguous.
+ */
+struct gpa_range {
+ u32 ByteCount;
+ u32 ByteOffset;
+ u64 PfnArray[0];
+};
+
+/*
+ * This is the format for an Establish Gpadl packet, which contains a handle by
+ * which this GPADL will be known and a set of GPA ranges associated with it.
+ * This can be converted to a MDL by the guest OS. If there are multiple GPA
+ * ranges, then the resulting MDL will be "chained," representing multiple VA
+ * ranges.
+ */
+struct vmestablish_gpadl {
+ struct vmpacket_descriptor d;
+ u32 Gpadl;
+ u32 RangeCount;
+ struct gpa_range Range[1];
+} __attribute__((packed));
+
+/*
+ * This is the format for a Teardown Gpadl packet, which indicates that the
+ * GPADL handle in the Establish Gpadl packet will never be referenced again.
+ */
+struct vmteardown_gpadl {
+ struct vmpacket_descriptor d;
+ u32 Gpadl;
+ u32 Reserved; /* for alignment to a 8-byte boundary */
+} __attribute__((packed));
+
+/*
+ * This is the format for a GPA-Direct packet, which contains a set of GPA
+ * ranges, in addition to commands and/or data.
+ */
+struct vmdata_gpa_direct {
+ struct vmpacket_descriptor d;
+ u32 Reserved;
+ u32 RangeCount;
+ struct gpa_range Range[1];
+} __attribute__((packed));
+
+/* This is the format for a Additional Data Packet. */
+struct vmadditional_data {
+ struct vmpacket_descriptor d;
+ u64 TotalBytes;
+ u32 ByteOffset;
+ u32 ByteCount;
+ unsigned char Data[1];
+} __attribute__((packed));
+
+union vmpacket_largest_possible_header {
+ struct vmpacket_descriptor SimpleHeader;
+ struct vmtransfer_page_packet_header TransferPageHeader;
+ struct vmgpadl_packet_header GpadlHeader;
+ struct vmadd_remove_transfer_page_set AddRemoveTransferPageHeader;
+ struct vmestablish_gpadl EstablishGpadlHeader;
+ struct vmteardown_gpadl TeardownGpadlHeader;
+ struct vmdata_gpa_direct DataGpaDirectHeader;
+};
+
+#define VMPACKET_DATA_START_ADDRESS(__packet) \
+ (void *)(((unsigned char *)__packet) + \
+ ((struct vmpacket_descriptor)__packet)->DataOffset8 * 8)
+
+#define VMPACKET_DATA_LENGTH(__packet) \
+ ((((struct vmpacket_descriptor)__packet)->Length8 - \
+ ((struct vmpacket_descriptor)__packet)->DataOffset8) * 8)
+
+#define VMPACKET_TRANSFER_MODE(__packet) \
+ (((struct IMPACT)__packet)->Type)
+
+enum vmbus_packet_type {
+ VmbusPacketTypeInvalid = 0x0,
+ VmbusPacketTypeSynch = 0x1,
+ VmbusPacketTypeAddTransferPageSet = 0x2,
+ VmbusPacketTypeRemoveTransferPageSet = 0x3,
+ VmbusPacketTypeEstablishGpadl = 0x4,
+ VmbusPacketTypeTearDownGpadl = 0x5,
+ VmbusPacketTypeDataInBand = 0x6,
+ VmbusPacketTypeDataUsingTransferPages = 0x7,
+ VmbusPacketTypeDataUsingGpadl = 0x8,
+ VmbusPacketTypeDataUsingGpaDirect = 0x9,
+ VmbusPacketTypeCancelRequest = 0xa,
+ VmbusPacketTypeCompletion = 0xb,
+ VmbusPacketTypeDataUsingAdditionalPackets = 0xc,
+ VmbusPacketTypeAdditionalData = 0xd
+};
+
+#define VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 1
+
+#endif
diff --git a/drivers/staging/hv/VmbusPrivate.h b/drivers/staging/hv/VmbusPrivate.h
new file mode 100644
index 000000000000..05ad2c9380d5
--- /dev/null
+++ b/drivers/staging/hv/VmbusPrivate.h
@@ -0,0 +1,134 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _VMBUS_PRIVATE_H_
+#define _VMBUS_PRIVATE_H_
+
+#include "Hv.h"
+#include "VmbusApi.h"
+#include "Channel.h"
+#include "ChannelMgmt.h"
+#include "ChannelInterface.h"
+#include "RingBuffer.h"
+#include <linux/list.h>
+
+
+/*
+ * Maximum channels is determined by the size of the interrupt page
+ * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt
+ * and the other is receive endpoint interrupt
+ */
+#define MAX_NUM_CHANNELS ((PAGE_SIZE >> 1) << 3) /* 16348 channels */
+
+/* The value here must be in multiple of 32 */
+/* TODO: Need to make this configurable */
+#define MAX_NUM_CHANNELS_SUPPORTED 256
+
+
+enum VMBUS_CONNECT_STATE {
+ Disconnected,
+ Connecting,
+ Connected,
+ Disconnecting
+};
+
+#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT
+
+struct VMBUS_CONNECTION {
+ enum VMBUS_CONNECT_STATE ConnectState;
+
+ atomic_t NextGpadlHandle;
+
+ /*
+ * Represents channel interrupts. Each bit position represents a
+ * channel. When a channel sends an interrupt via VMBUS, it finds its
+ * bit in the sendInterruptPage, set it and calls Hv to generate a port
+ * event. The other end receives the port event and parse the
+ * recvInterruptPage to see which bit is set
+ */
+ void *InterruptPage;
+ void *SendInterruptPage;
+ void *RecvInterruptPage;
+
+ /*
+ * 2 pages - 1st page for parent->child notification and 2nd
+ * is child->parent notification
+ */
+ void *MonitorPages;
+ struct list_head ChannelMsgList;
+ spinlock_t channelmsg_lock;
+
+ /* List of channels */
+ struct list_head ChannelList;
+ spinlock_t channel_lock;
+
+ struct workqueue_struct *WorkQueue;
+};
+
+
+struct VMBUS_MSGINFO {
+ /* Bookkeeping stuff */
+ struct list_head MsgListEntry;
+
+ /* Synchronize the request/response if needed */
+ struct osd_waitevent *WaitEvent;
+
+ /* The message itself */
+ unsigned char Msg[0];
+};
+
+
+extern struct VMBUS_CONNECTION gVmbusConnection;
+
+/* General vmbus interface */
+
+struct hv_device *VmbusChildDeviceCreate(struct hv_guid *deviceType,
+ struct hv_guid *deviceInstance,
+ void *context);
+
+int VmbusChildDeviceAdd(struct hv_device *Device);
+
+void VmbusChildDeviceRemove(struct hv_device *Device);
+
+/* static void */
+/* VmbusChildDeviceDestroy( */
+/* struct hv_device *); */
+
+struct vmbus_channel *GetChannelFromRelId(u32 relId);
+
+
+/* Connection interface */
+
+int VmbusConnect(void);
+
+int VmbusDisconnect(void);
+
+int VmbusPostMessage(void *buffer, size_t bufSize);
+
+int VmbusSetEvent(u32 childRelId);
+
+void VmbusOnEvents(void);
+
+
+#endif /* _VMBUS_PRIVATE_H_ */
diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c
new file mode 100644
index 000000000000..99c49261a8b4
--- /dev/null
+++ b/drivers/staging/hv/blkvsc_drv.c
@@ -0,0 +1,1511 @@
+/*
+ * 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:
+ * Hank Janssen <hjanssen@microsoft.com>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/blkdev.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dbg.h>
+#include "osd.h"
+#include "logging.h"
+#include "vmbus.h"
+#include "StorVscApi.h"
+
+
+#define BLKVSC_MINORS 64
+
+enum blkvsc_device_type {
+ UNKNOWN_DEV_TYPE,
+ HARDDISK_TYPE,
+ DVD_TYPE,
+};
+
+/*
+ * This request ties the struct request and struct
+ * blkvsc_request/hv_storvsc_request together A struct request may be
+ * represented by 1 or more struct blkvsc_request
+ */
+struct blkvsc_request_group {
+ int outstanding;
+ int status;
+ struct list_head blkvsc_req_list; /* list of blkvsc_requests */
+};
+
+struct blkvsc_request {
+ /* blkvsc_request_group.blkvsc_req_list */
+ struct list_head req_entry;
+
+ /* block_device_context.pending_list */
+ struct list_head pend_entry;
+
+ /* This may be null if we generate a request internally */
+ struct request *req;
+
+ struct block_device_context *dev;
+
+ /* The group this request is part of. Maybe null */
+ struct blkvsc_request_group *group;
+
+ wait_queue_head_t wevent;
+ int cond;
+
+ int write;
+ sector_t sector_start;
+ unsigned long sector_count;
+
+ unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
+ unsigned char cmd_len;
+ unsigned char cmnd[MAX_COMMAND_SIZE];
+
+ struct hv_storvsc_request request;
+ /*
+ * !!!DO NOT ADD ANYTHING BELOW HERE!!! Otherwise, memory can overlap,
+ * because - The extension buffer falls right here and is pointed to by
+ * request.Extension;
+ * Which sounds like a horrible idea, who designed this?
+ */
+};
+
+/* Per device structure */
+struct block_device_context {
+ /* point back to our device context */
+ struct device_context *device_ctx;
+ struct kmem_cache *request_pool;
+ spinlock_t lock;
+ struct gendisk *gd;
+ enum blkvsc_device_type device_type;
+ struct list_head pending_list;
+
+ unsigned char device_id[64];
+ unsigned int device_id_len;
+ int num_outstanding_reqs;
+ int shutting_down;
+ int media_not_present;
+ unsigned int sector_size;
+ sector_t capacity;
+ unsigned int port;
+ unsigned char path;
+ unsigned char target;
+ int users;
+};
+
+/* Per driver */
+struct blkvsc_driver_context {
+ /* !! These must be the first 2 fields !! */
+ /* FIXME this is a bug! */
+ struct driver_context drv_ctx;
+ struct storvsc_driver_object drv_obj;
+};
+
+/* Static decl */
+static int blkvsc_probe(struct device *dev);
+static int blkvsc_remove(struct device *device);
+static void blkvsc_shutdown(struct device *device);
+
+static int blkvsc_open(struct block_device *bdev, fmode_t mode);
+static int blkvsc_release(struct gendisk *disk, fmode_t mode);
+static int blkvsc_media_changed(struct gendisk *gd);
+static int blkvsc_revalidate_disk(struct gendisk *gd);
+static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg);
+static int blkvsc_ioctl(struct block_device *bd, fmode_t mode,
+ unsigned cmd, unsigned long argument);
+static void blkvsc_request(struct request_queue *queue);
+static void blkvsc_request_completion(struct hv_storvsc_request *request);
+static int blkvsc_do_request(struct block_device_context *blkdev,
+ struct request *req);
+static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req,
+ void (*request_completion)(struct hv_storvsc_request *));
+static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req);
+static void blkvsc_cmd_completion(struct hv_storvsc_request *request);
+static int blkvsc_do_inquiry(struct block_device_context *blkdev);
+static int blkvsc_do_read_capacity(struct block_device_context *blkdev);
+static int blkvsc_do_read_capacity16(struct block_device_context *blkdev);
+static int blkvsc_do_flush(struct block_device_context *blkdev);
+static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev);
+static int blkvsc_do_pending_reqs(struct block_device_context *blkdev);
+
+
+static int blkvsc_ringbuffer_size = BLKVSC_RING_BUFFER_SIZE;
+
+/* The one and only one */
+static struct blkvsc_driver_context g_blkvsc_drv;
+
+static struct block_device_operations block_ops = {
+ .owner = THIS_MODULE,
+ .open = blkvsc_open,
+ .release = blkvsc_release,
+ .media_changed = blkvsc_media_changed,
+ .revalidate_disk = blkvsc_revalidate_disk,
+ .getgeo = blkvsc_getgeo,
+ .ioctl = blkvsc_ioctl,
+};
+
+/**
+ * blkvsc_drv_init - BlkVsc driver initialization.
+ */
+static int blkvsc_drv_init(int (*drv_init)(struct hv_driver *drv))
+{
+ struct storvsc_driver_object *storvsc_drv_obj = &g_blkvsc_drv.drv_obj;
+ struct driver_context *drv_ctx = &g_blkvsc_drv.drv_ctx;
+ int ret;
+
+ DPRINT_ENTER(BLKVSC_DRV);
+
+ vmbus_get_interface(&storvsc_drv_obj->Base.VmbusChannelInterface);
+
+ storvsc_drv_obj->RingBufferSize = blkvsc_ringbuffer_size;
+
+ /* Callback to client driver to complete the initialization */
+ drv_init(&storvsc_drv_obj->Base);
+
+ drv_ctx->driver.name = storvsc_drv_obj->Base.name;
+ memcpy(&drv_ctx->class_id, &storvsc_drv_obj->Base.deviceType,
+ sizeof(struct hv_guid));
+
+ drv_ctx->probe = blkvsc_probe;
+ drv_ctx->remove = blkvsc_remove;
+ drv_ctx->shutdown = blkvsc_shutdown;
+
+ /* The driver belongs to vmbus */
+ ret = vmbus_child_driver_register(drv_ctx);
+
+ DPRINT_EXIT(BLKVSC_DRV);
+
+ return ret;
+}
+
+static int blkvsc_drv_exit_cb(struct device *dev, void *data)
+{
+ struct device **curr = (struct device **)data;
+ *curr = dev;
+ return 1; /* stop iterating */
+}
+
+static void blkvsc_drv_exit(void)
+{
+ struct storvsc_driver_object *storvsc_drv_obj = &g_blkvsc_drv.drv_obj;
+ struct driver_context *drv_ctx = &g_blkvsc_drv.drv_ctx;
+ struct device *current_dev;
+ int ret;
+
+ DPRINT_ENTER(BLKVSC_DRV);
+
+ while (1) {
+ current_dev = NULL;
+
+ /* Get the device */
+ ret = driver_for_each_device(&drv_ctx->driver, NULL,
+ (void *) &current_dev,
+ blkvsc_drv_exit_cb);
+
+ if (ret)
+ DPRINT_WARN(BLKVSC_DRV,
+ "driver_for_each_device returned %d", ret);
+
+
+ if (current_dev == NULL)
+ break;
+
+ /* Initiate removal from the top-down */
+ device_unregister(current_dev);
+ }
+
+ if (storvsc_drv_obj->Base.OnCleanup)
+ storvsc_drv_obj->Base.OnCleanup(&storvsc_drv_obj->Base);
+
+ vmbus_child_driver_unregister(drv_ctx);
+
+ DPRINT_EXIT(BLKVSC_DRV);
+
+ return;
+}
+
+/**
+ * blkvsc_probe - Add a new device for this driver
+ */
+static int blkvsc_probe(struct device *device)
+{
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device->driver);
+ struct blkvsc_driver_context *blkvsc_drv_ctx =
+ (struct blkvsc_driver_context *)driver_ctx;
+ struct storvsc_driver_object *storvsc_drv_obj =
+ &blkvsc_drv_ctx->drv_obj;
+ struct device_context *device_ctx = device_to_device_context(device);
+ struct hv_device *device_obj = &device_ctx->device_obj;
+
+ struct block_device_context *blkdev = NULL;
+ struct storvsc_device_info device_info;
+ int major = 0;
+ int devnum = 0;
+ int ret = 0;
+ static int ide0_registered;
+ static int ide1_registered;
+
+ DPRINT_ENTER(BLKVSC_DRV);
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_probe - enter");
+
+ if (!storvsc_drv_obj->Base.OnDeviceAdd) {
+ DPRINT_ERR(BLKVSC_DRV, "OnDeviceAdd() not set");
+ ret = -1;
+ goto Cleanup;
+ }
+
+ blkdev = kzalloc(sizeof(struct block_device_context), GFP_KERNEL);
+ if (!blkdev) {
+ ret = -ENOMEM;
+ goto Cleanup;
+ }
+
+ INIT_LIST_HEAD(&blkdev->pending_list);
+
+ /* Initialize what we can here */
+ spin_lock_init(&blkdev->lock);
+
+ ASSERT(sizeof(struct blkvsc_request_group) <=
+ sizeof(struct blkvsc_request));
+
+ blkdev->request_pool = kmem_cache_create(dev_name(&device_ctx->device),
+ sizeof(struct blkvsc_request) +
+ storvsc_drv_obj->RequestExtSize, 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!blkdev->request_pool) {
+ ret = -ENOMEM;
+ goto Cleanup;
+ }
+
+
+ /* Call to the vsc driver to add the device */
+ ret = storvsc_drv_obj->Base.OnDeviceAdd(device_obj, &device_info);
+ if (ret != 0) {
+ DPRINT_ERR(BLKVSC_DRV, "unable to add blkvsc device");
+ goto Cleanup;
+ }
+
+ blkdev->device_ctx = device_ctx;
+ /* this identified the device 0 or 1 */
+ blkdev->target = device_info.TargetId;
+ /* this identified the ide ctrl 0 or 1 */
+ blkdev->path = device_info.PathId;
+
+ dev_set_drvdata(device, blkdev);
+
+ /* Calculate the major and device num */
+ if (blkdev->path == 0) {
+ major = IDE0_MAJOR;
+ devnum = blkdev->path + blkdev->target; /* 0 or 1 */
+
+ if (!ide0_registered) {
+ ret = register_blkdev(major, "ide");
+ if (ret != 0) {
+ DPRINT_ERR(BLKVSC_DRV,
+ "register_blkdev() failed! ret %d",
+ ret);
+ goto Remove;
+ }
+
+ ide0_registered = 1;
+ }
+ } else if (blkdev->path == 1) {
+ major = IDE1_MAJOR;
+ devnum = blkdev->path + blkdev->target + 1; /* 2 or 3 */
+
+ if (!ide1_registered) {
+ ret = register_blkdev(major, "ide");
+ if (ret != 0) {
+ DPRINT_ERR(BLKVSC_DRV,
+ "register_blkdev() failed! ret %d",
+ ret);
+ goto Remove;
+ }
+
+ ide1_registered = 1;
+ }
+ } else {
+ DPRINT_ERR(BLKVSC_DRV, "invalid pathid");
+ ret = -1;
+ goto Cleanup;
+ }
+
+ DPRINT_INFO(BLKVSC_DRV, "blkvsc registered for major %d!!", major);
+
+ blkdev->gd = alloc_disk(BLKVSC_MINORS);
+ if (!blkdev->gd) {
+ DPRINT_ERR(BLKVSC_DRV, "register_blkdev() failed! ret %d", ret);
+ ret = -1;
+ goto Cleanup;
+ }
+
+ blkdev->gd->queue = blk_init_queue(blkvsc_request, &blkdev->lock);
+
+ blk_queue_max_segment_size(blkdev->gd->queue, PAGE_SIZE);
+ blk_queue_max_phys_segments(blkdev->gd->queue,
+ MAX_MULTIPAGE_BUFFER_COUNT);
+ blk_queue_max_hw_segments(blkdev->gd->queue,
+ MAX_MULTIPAGE_BUFFER_COUNT);
+ blk_queue_segment_boundary(blkdev->gd->queue, PAGE_SIZE-1);
+ blk_queue_bounce_limit(blkdev->gd->queue, BLK_BOUNCE_ANY);
+ blk_queue_dma_alignment(blkdev->gd->queue, 511);
+
+ blkdev->gd->major = major;
+ if (devnum == 1 || devnum == 3)
+ blkdev->gd->first_minor = BLKVSC_MINORS;
+ else
+ blkdev->gd->first_minor = 0;
+ blkdev->gd->fops = &block_ops;
+ blkdev->gd->private_data = blkdev;
+ sprintf(blkdev->gd->disk_name, "hd%c", 'a' + devnum);
+
+ blkvsc_do_inquiry(blkdev);
+ if (blkdev->device_type == DVD_TYPE) {
+ set_disk_ro(blkdev->gd, 1);
+ blkdev->gd->flags |= GENHD_FL_REMOVABLE;
+ blkvsc_do_read_capacity(blkdev);
+ } else {
+ blkvsc_do_read_capacity16(blkdev);
+ }
+
+ set_capacity(blkdev->gd, blkdev->capacity * (blkdev->sector_size/512));
+ blk_queue_logical_block_size(blkdev->gd->queue, blkdev->sector_size);
+ /* go! */
+ add_disk(blkdev->gd);
+
+ DPRINT_INFO(BLKVSC_DRV, "%s added!! capacity %lu sector_size %d",
+ blkdev->gd->disk_name, (unsigned long)blkdev->capacity,
+ blkdev->sector_size);
+
+ return ret;
+
+Remove:
+ storvsc_drv_obj->Base.OnDeviceRemove(device_obj);
+
+Cleanup:
+ if (blkdev) {
+ if (blkdev->request_pool) {
+ kmem_cache_destroy(blkdev->request_pool);
+ blkdev->request_pool = NULL;
+ }
+ kfree(blkdev);
+ blkdev = NULL;
+ }
+
+ DPRINT_EXIT(BLKVSC_DRV);
+
+ return ret;
+}
+
+static void blkvsc_shutdown(struct device *device)
+{
+ struct block_device_context *blkdev = dev_get_drvdata(device);
+ unsigned long flags;
+
+ if (!blkdev)
+ return;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_shutdown - users %d disk %s\n",
+ blkdev->users, blkdev->gd->disk_name);
+
+ spin_lock_irqsave(&blkdev->lock, flags);
+
+ blkdev->shutting_down = 1;
+
+ blk_stop_queue(blkdev->gd->queue);
+
+ spin_unlock_irqrestore(&blkdev->lock, flags);
+
+ while (blkdev->num_outstanding_reqs) {
+ DPRINT_INFO(STORVSC, "waiting for %d requests to complete...",
+ blkdev->num_outstanding_reqs);
+ udelay(100);
+ }
+
+ blkvsc_do_flush(blkdev);
+
+ spin_lock_irqsave(&blkdev->lock, flags);
+
+ blkvsc_cancel_pending_reqs(blkdev);
+
+ spin_unlock_irqrestore(&blkdev->lock, flags);
+}
+
+static int blkvsc_do_flush(struct block_device_context *blkdev)
+{
+ struct blkvsc_request *blkvsc_req;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_flush()\n");
+
+ if (blkdev->device_type != HARDDISK_TYPE)
+ return 0;
+
+ blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL);
+ if (!blkvsc_req)
+ return -ENOMEM;
+
+ memset(blkvsc_req, 0, sizeof(struct blkvsc_request));
+ init_waitqueue_head(&blkvsc_req->wevent);
+ blkvsc_req->dev = blkdev;
+ blkvsc_req->req = NULL;
+ blkvsc_req->write = 0;
+
+ blkvsc_req->request.DataBuffer.PfnArray[0] = 0;
+ blkvsc_req->request.DataBuffer.Offset = 0;
+ blkvsc_req->request.DataBuffer.Length = 0;
+
+ blkvsc_req->cmnd[0] = SYNCHRONIZE_CACHE;
+ blkvsc_req->cmd_len = 10;
+
+ /*
+ * Set this here since the completion routine may be invoked and
+ * completed before we return
+ */
+ blkvsc_req->cond = 0;
+ blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion);
+
+ wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond);
+
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+
+ return 0;
+}
+
+/* Do a scsi INQUIRY cmd here to get the device type (ie disk or dvd) */
+static int blkvsc_do_inquiry(struct block_device_context *blkdev)
+{
+ struct blkvsc_request *blkvsc_req;
+ struct page *page_buf;
+ unsigned char *buf;
+ unsigned char device_type;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_inquiry()\n");
+
+ blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL);
+ if (!blkvsc_req)
+ return -ENOMEM;
+
+ memset(blkvsc_req, 0, sizeof(struct blkvsc_request));
+ page_buf = alloc_page(GFP_KERNEL);
+ if (!page_buf) {
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&blkvsc_req->wevent);
+ blkvsc_req->dev = blkdev;
+ blkvsc_req->req = NULL;
+ blkvsc_req->write = 0;
+
+ blkvsc_req->request.DataBuffer.PfnArray[0] = page_to_pfn(page_buf);
+ blkvsc_req->request.DataBuffer.Offset = 0;
+ blkvsc_req->request.DataBuffer.Length = 64;
+
+ blkvsc_req->cmnd[0] = INQUIRY;
+ blkvsc_req->cmnd[1] = 0x1; /* Get product data */
+ blkvsc_req->cmnd[2] = 0x83; /* mode page 83 */
+ blkvsc_req->cmnd[4] = 64;
+ blkvsc_req->cmd_len = 6;
+
+ /*
+ * Set this here since the completion routine may be invoked and
+ * completed before we return
+ */
+ blkvsc_req->cond = 0;
+
+ blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion);
+
+ DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n",
+ blkvsc_req, blkvsc_req->cond);
+
+ wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond);
+
+ buf = kmap(page_buf);
+
+ /* print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, 64); */
+ /* be to le */
+ device_type = buf[0] & 0x1F;
+
+ if (device_type == 0x0) {
+ blkdev->device_type = HARDDISK_TYPE;
+ } else if (device_type == 0x5) {
+ blkdev->device_type = DVD_TYPE;
+ } else {
+ /* TODO: this is currently unsupported device type */
+ blkdev->device_type = UNKNOWN_DEV_TYPE;
+ }
+
+ DPRINT_DBG(BLKVSC_DRV, "device type %d \n", device_type);
+
+ blkdev->device_id_len = buf[7];
+ if (blkdev->device_id_len > 64)
+ blkdev->device_id_len = 64;
+
+ memcpy(blkdev->device_id, &buf[8], blkdev->device_id_len);
+ /* printk_hex_dump_bytes("", DUMP_PREFIX_NONE, blkdev->device_id,
+ * blkdev->device_id_len); */
+
+ kunmap(page_buf);
+
+ __free_page(page_buf);
+
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+
+ return 0;
+}
+
+/* Do a scsi READ_CAPACITY cmd here to get the size of the disk */
+static int blkvsc_do_read_capacity(struct block_device_context *blkdev)
+{
+ struct blkvsc_request *blkvsc_req;
+ struct page *page_buf;
+ unsigned char *buf;
+ struct scsi_sense_hdr sense_hdr;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_read_capacity()\n");
+
+ blkdev->sector_size = 0;
+ blkdev->capacity = 0;
+ blkdev->media_not_present = 0; /* assume a disk is present */
+
+ blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL);
+ if (!blkvsc_req)
+ return -ENOMEM;
+
+ memset(blkvsc_req, 0, sizeof(struct blkvsc_request));
+ page_buf = alloc_page(GFP_KERNEL);
+ if (!page_buf) {
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&blkvsc_req->wevent);
+ blkvsc_req->dev = blkdev;
+ blkvsc_req->req = NULL;
+ blkvsc_req->write = 0;
+
+ blkvsc_req->request.DataBuffer.PfnArray[0] = page_to_pfn(page_buf);
+ blkvsc_req->request.DataBuffer.Offset = 0;
+ blkvsc_req->request.DataBuffer.Length = 8;
+
+ blkvsc_req->cmnd[0] = READ_CAPACITY;
+ blkvsc_req->cmd_len = 16;
+
+ /*
+ * Set this here since the completion routine may be invoked
+ * and completed before we return
+ */
+ blkvsc_req->cond = 0;
+
+ blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion);
+
+ DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n",
+ blkvsc_req, blkvsc_req->cond);
+
+ wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond);
+
+ /* check error */
+ if (blkvsc_req->request.Status) {
+ scsi_normalize_sense(blkvsc_req->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, &sense_hdr);
+
+ if (sense_hdr.asc == 0x3A) {
+ /* Medium not present */
+ blkdev->media_not_present = 1;
+ }
+ return 0;
+ }
+ buf = kmap(page_buf);
+
+ /* be to le */
+ blkdev->capacity = ((buf[0] << 24) | (buf[1] << 16) |
+ (buf[2] << 8) | buf[3]) + 1;
+ blkdev->sector_size = (buf[4] << 24) | (buf[5] << 16) |
+ (buf[6] << 8) | buf[7];
+
+ kunmap(page_buf);
+
+ __free_page(page_buf);
+
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+
+ return 0;
+}
+
+static int blkvsc_do_read_capacity16(struct block_device_context *blkdev)
+{
+ struct blkvsc_request *blkvsc_req;
+ struct page *page_buf;
+ unsigned char *buf;
+ struct scsi_sense_hdr sense_hdr;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_do_read_capacity16()\n");
+
+ blkdev->sector_size = 0;
+ blkdev->capacity = 0;
+ blkdev->media_not_present = 0; /* assume a disk is present */
+
+ blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_KERNEL);
+ if (!blkvsc_req)
+ return -ENOMEM;
+
+ memset(blkvsc_req, 0, sizeof(struct blkvsc_request));
+ page_buf = alloc_page(GFP_KERNEL);
+ if (!page_buf) {
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&blkvsc_req->wevent);
+ blkvsc_req->dev = blkdev;
+ blkvsc_req->req = NULL;
+ blkvsc_req->write = 0;
+
+ blkvsc_req->request.DataBuffer.PfnArray[0] = page_to_pfn(page_buf);
+ blkvsc_req->request.DataBuffer.Offset = 0;
+ blkvsc_req->request.DataBuffer.Length = 12;
+
+ blkvsc_req->cmnd[0] = 0x9E; /* READ_CAPACITY16; */
+ blkvsc_req->cmd_len = 16;
+
+ /*
+ * Set this here since the completion routine may be invoked
+ * and completed before we return
+ */
+ blkvsc_req->cond = 0;
+
+ blkvsc_submit_request(blkvsc_req, blkvsc_cmd_completion);
+
+ DPRINT_DBG(BLKVSC_DRV, "waiting %p to complete - cond %d\n",
+ blkvsc_req, blkvsc_req->cond);
+
+ wait_event_interruptible(blkvsc_req->wevent, blkvsc_req->cond);
+
+ /* check error */
+ if (blkvsc_req->request.Status) {
+ scsi_normalize_sense(blkvsc_req->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, &sense_hdr);
+ if (sense_hdr.asc == 0x3A) {
+ /* Medium not present */
+ blkdev->media_not_present = 1;
+ }
+ return 0;
+ }
+ buf = kmap(page_buf);
+
+ /* be to le */
+ blkdev->capacity = be64_to_cpu(*(unsigned long long *) &buf[0]) + 1;
+ blkdev->sector_size = be32_to_cpu(*(unsigned int *)&buf[8]);
+
+#if 0
+ blkdev->capacity = ((buf[0] << 24) | (buf[1] << 16) |
+ (buf[2] << 8) | buf[3]) + 1;
+ blkdev->sector_size = (buf[4] << 24) | (buf[5] << 16) |
+ (buf[6] << 8) | buf[7];
+#endif
+
+ kunmap(page_buf);
+
+ __free_page(page_buf);
+
+ kmem_cache_free(blkvsc_req->dev->request_pool, blkvsc_req);
+
+ return 0;
+}
+
+/**
+ * blkvsc_remove() - Callback when our device is removed
+ */
+static int blkvsc_remove(struct device *device)
+{
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device->driver);
+ struct blkvsc_driver_context *blkvsc_drv_ctx =
+ (struct blkvsc_driver_context *)driver_ctx;
+ struct storvsc_driver_object *storvsc_drv_obj =
+ &blkvsc_drv_ctx->drv_obj;
+ struct device_context *device_ctx = device_to_device_context(device);
+ struct hv_device *device_obj = &device_ctx->device_obj;
+ struct block_device_context *blkdev = dev_get_drvdata(device);
+ unsigned long flags;
+ int ret;
+
+ DPRINT_ENTER(BLKVSC_DRV);
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_remove()\n");
+
+ if (!storvsc_drv_obj->Base.OnDeviceRemove) {
+ DPRINT_EXIT(BLKVSC_DRV);
+ return -1;
+ }
+
+ /*
+ * Call to the vsc driver to let it know that the device is being
+ * removed
+ */
+ ret = storvsc_drv_obj->Base.OnDeviceRemove(device_obj);
+ if (ret != 0) {
+ /* TODO: */
+ DPRINT_ERR(BLKVSC_DRV,
+ "unable to remove blkvsc device (ret %d)", ret);
+ }
+
+ /* Get to a known state */
+ spin_lock_irqsave(&blkdev->lock, flags);
+
+ blkdev->shutting_down = 1;
+
+ blk_stop_queue(blkdev->gd->queue);
+
+ spin_unlock_irqrestore(&blkdev->lock, flags);
+
+ while (blkdev->num_outstanding_reqs) {
+ DPRINT_INFO(STORVSC, "waiting for %d requests to complete...",
+ blkdev->num_outstanding_reqs);
+ udelay(100);
+ }
+
+ blkvsc_do_flush(blkdev);
+
+ spin_lock_irqsave(&blkdev->lock, flags);
+
+ blkvsc_cancel_pending_reqs(blkdev);
+
+ spin_unlock_irqrestore(&blkdev->lock, flags);
+
+ blk_cleanup_queue(blkdev->gd->queue);
+
+ del_gendisk(blkdev->gd);
+
+ kmem_cache_destroy(blkdev->request_pool);
+
+ kfree(blkdev);
+
+ DPRINT_EXIT(BLKVSC_DRV);
+
+ return ret;
+}
+
+static void blkvsc_init_rw(struct blkvsc_request *blkvsc_req)
+{
+ ASSERT(blkvsc_req->req);
+ ASSERT(blkvsc_req->sector_count <= (MAX_MULTIPAGE_BUFFER_COUNT*8));
+
+ blkvsc_req->cmd_len = 16;
+
+ if (blkvsc_req->sector_start > 0xffffffff) {
+ if (rq_data_dir(blkvsc_req->req)) {
+ blkvsc_req->write = 1;
+ blkvsc_req->cmnd[0] = WRITE_16;
+ } else {
+ blkvsc_req->write = 0;
+ blkvsc_req->cmnd[0] = READ_16;
+ }
+
+ blkvsc_req->cmnd[1] |= blk_fua_rq(blkvsc_req->req) ? 0x8 : 0;
+
+ *(unsigned long long *)&blkvsc_req->cmnd[2] =
+ cpu_to_be64(blkvsc_req->sector_start);
+ *(unsigned int *)&blkvsc_req->cmnd[10] =
+ cpu_to_be32(blkvsc_req->sector_count);
+ } else if ((blkvsc_req->sector_count > 0xff) ||
+ (blkvsc_req->sector_start > 0x1fffff)) {
+ if (rq_data_dir(blkvsc_req->req)) {
+ blkvsc_req->write = 1;
+ blkvsc_req->cmnd[0] = WRITE_10;
+ } else {
+ blkvsc_req->write = 0;
+ blkvsc_req->cmnd[0] = READ_10;
+ }
+
+ blkvsc_req->cmnd[1] |= blk_fua_rq(blkvsc_req->req) ? 0x8 : 0;
+
+ *(unsigned int *)&blkvsc_req->cmnd[2] =
+ cpu_to_be32(blkvsc_req->sector_start);
+ *(unsigned short *)&blkvsc_req->cmnd[7] =
+ cpu_to_be16(blkvsc_req->sector_count);
+ } else {
+ if (rq_data_dir(blkvsc_req->req)) {
+ blkvsc_req->write = 1;
+ blkvsc_req->cmnd[0] = WRITE_6;
+ } else {
+ blkvsc_req->write = 0;
+ blkvsc_req->cmnd[0] = READ_6;
+ }
+
+ *(unsigned int *)&blkvsc_req->cmnd[1] =
+ cpu_to_be32(blkvsc_req->sector_start) >> 8;
+ blkvsc_req->cmnd[1] &= 0x1f;
+ blkvsc_req->cmnd[4] = (unsigned char)blkvsc_req->sector_count;
+ }
+}
+
+static int blkvsc_submit_request(struct blkvsc_request *blkvsc_req,
+ void (*request_completion)(struct hv_storvsc_request *))
+{
+ struct block_device_context *blkdev = blkvsc_req->dev;
+ struct device_context *device_ctx = blkdev->device_ctx;
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device_ctx->device.driver);
+ struct blkvsc_driver_context *blkvsc_drv_ctx =
+ (struct blkvsc_driver_context *)driver_ctx;
+ struct storvsc_driver_object *storvsc_drv_obj =
+ &blkvsc_drv_ctx->drv_obj;
+ struct hv_storvsc_request *storvsc_req;
+ int ret;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_submit_request() - "
+ "req %p type %s start_sector %lu count %ld offset %d "
+ "len %d\n", blkvsc_req,
+ (blkvsc_req->write) ? "WRITE" : "READ",
+ (unsigned long) blkvsc_req->sector_start,
+ blkvsc_req->sector_count,
+ blkvsc_req->request.DataBuffer.Offset,
+ blkvsc_req->request.DataBuffer.Length);
+#if 0
+ for (i = 0; i < (blkvsc_req->request.DataBuffer.Length >> 12); i++) {
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_submit_request() - "
+ "req %p pfn[%d] %llx\n",
+ blkvsc_req, i,
+ blkvsc_req->request.DataBuffer.PfnArray[i]);
+ }
+#endif
+
+ storvsc_req = &blkvsc_req->request;
+ storvsc_req->Extension = (void *)((unsigned long)blkvsc_req +
+ sizeof(struct blkvsc_request));
+
+ storvsc_req->Type = blkvsc_req->write ? WRITE_TYPE : READ_TYPE;
+
+ storvsc_req->OnIOCompletion = request_completion;
+ storvsc_req->Context = blkvsc_req;
+
+ storvsc_req->Host = blkdev->port;
+ storvsc_req->Bus = blkdev->path;
+ storvsc_req->TargetId = blkdev->target;
+ storvsc_req->LunId = 0; /* this is not really used at all */
+
+ storvsc_req->CdbLen = blkvsc_req->cmd_len;
+ storvsc_req->Cdb = blkvsc_req->cmnd;
+
+ storvsc_req->SenseBuffer = blkvsc_req->sense_buffer;
+ storvsc_req->SenseBufferSize = SCSI_SENSE_BUFFERSIZE;
+
+ ret = storvsc_drv_obj->OnIORequest(&blkdev->device_ctx->device_obj,
+ &blkvsc_req->request);
+ if (ret == 0)
+ blkdev->num_outstanding_reqs++;
+
+ return ret;
+}
+
+/*
+ * We break the request into 1 or more blkvsc_requests and submit
+ * them. If we cant submit them all, we put them on the
+ * pending_list. The blkvsc_request() will work on the pending_list.
+ */
+static int blkvsc_do_request(struct block_device_context *blkdev,
+ struct request *req)
+{
+ struct bio *bio = NULL;
+ struct bio_vec *bvec = NULL;
+ struct bio_vec *prev_bvec = NULL;
+ struct blkvsc_request *blkvsc_req = NULL;
+ struct blkvsc_request *tmp;
+ int databuf_idx = 0;
+ int seg_idx = 0;
+ sector_t start_sector;
+ unsigned long num_sectors = 0;
+ int ret = 0;
+ int pending = 0;
+ struct blkvsc_request_group *group = NULL;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkdev %p req %p sect %lu \n", blkdev, req,
+ (unsigned long)blk_rq_pos(req));
+
+ /* Create a group to tie req to list of blkvsc_reqs */
+ group = kmem_cache_alloc(blkdev->request_pool, GFP_ATOMIC);
+ if (!group)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&group->blkvsc_req_list);
+ group->outstanding = group->status = 0;
+
+ start_sector = blk_rq_pos(req);
+
+ /* foreach bio in the request */
+ if (req->bio) {
+ for (bio = req->bio; bio; bio = bio->bi_next) {
+ /*
+ * Map this bio into an existing or new storvsc request
+ */
+ bio_for_each_segment(bvec, bio, seg_idx) {
+ DPRINT_DBG(BLKVSC_DRV, "bio_for_each_segment() "
+ "- req %p bio %p bvec %p seg_idx %d "
+ "databuf_idx %d\n", req, bio, bvec,
+ seg_idx, databuf_idx);
+
+ /* Get a new storvsc request */
+ /* 1st-time */
+ if ((!blkvsc_req) ||
+ (databuf_idx >= MAX_MULTIPAGE_BUFFER_COUNT)
+ /* hole at the begin of page */
+ || (bvec->bv_offset != 0) ||
+ /* hold at the end of page */
+ (prev_bvec &&
+ (prev_bvec->bv_len != PAGE_SIZE))) {
+ /* submit the prev one */
+ if (blkvsc_req) {
+ blkvsc_req->sector_start = start_sector;
+ sector_div(blkvsc_req->sector_start, (blkdev->sector_size >> 9));
+
+ blkvsc_req->sector_count = num_sectors / (blkdev->sector_size >> 9);
+ blkvsc_init_rw(blkvsc_req);
+ }
+
+ /*
+ * Create new blkvsc_req to represent
+ * the current bvec
+ */
+ blkvsc_req = kmem_cache_alloc(blkdev->request_pool, GFP_ATOMIC);
+ if (!blkvsc_req) {
+ /* free up everything */
+ list_for_each_entry_safe(
+ blkvsc_req, tmp,
+ &group->blkvsc_req_list,
+ req_entry) {
+ list_del(&blkvsc_req->req_entry);
+ kmem_cache_free(blkdev->request_pool, blkvsc_req);
+ }
+
+ kmem_cache_free(blkdev->request_pool, group);
+ return -ENOMEM;
+ }
+
+ memset(blkvsc_req, 0,
+ sizeof(struct blkvsc_request));
+
+ blkvsc_req->dev = blkdev;
+ blkvsc_req->req = req;
+ blkvsc_req->request.DataBuffer.Offset = bvec->bv_offset;
+ blkvsc_req->request.DataBuffer.Length = 0;
+
+ /* Add to the group */
+ blkvsc_req->group = group;
+ blkvsc_req->group->outstanding++;
+ list_add_tail(&blkvsc_req->req_entry,
+ &blkvsc_req->group->blkvsc_req_list);
+
+ start_sector += num_sectors;
+ num_sectors = 0;
+ databuf_idx = 0;
+ }
+
+ /* Add the curr bvec/segment to the curr blkvsc_req */
+ blkvsc_req->request.DataBuffer.PfnArray[databuf_idx] = page_to_pfn(bvec->bv_page);
+ blkvsc_req->request.DataBuffer.Length += bvec->bv_len;
+
+ prev_bvec = bvec;
+
+ databuf_idx++;
+ num_sectors += bvec->bv_len >> 9;
+
+ } /* bio_for_each_segment */
+
+ } /* rq_for_each_bio */
+ }
+
+ /* Handle the last one */
+ if (blkvsc_req) {
+ DPRINT_DBG(BLKVSC_DRV, "blkdev %p req %p group %p count %d\n",
+ blkdev, req, blkvsc_req->group,
+ blkvsc_req->group->outstanding);
+
+ blkvsc_req->sector_start = start_sector;
+ sector_div(blkvsc_req->sector_start,
+ (blkdev->sector_size >> 9));
+
+ blkvsc_req->sector_count = num_sectors /
+ (blkdev->sector_size >> 9);
+
+ blkvsc_init_rw(blkvsc_req);
+ }
+
+ list_for_each_entry(blkvsc_req, &group->blkvsc_req_list, req_entry) {
+ if (pending) {
+ DPRINT_DBG(BLKVSC_DRV, "adding blkvsc_req to "
+ "pending_list - blkvsc_req %p start_sect %lu"
+ " sect_count %ld (%lu %ld)\n", blkvsc_req,
+ (unsigned long)blkvsc_req->sector_start,
+ blkvsc_req->sector_count,
+ (unsigned long)start_sector,
+ (unsigned long)num_sectors);
+
+ list_add_tail(&blkvsc_req->pend_entry,
+ &blkdev->pending_list);
+ } else {
+ ret = blkvsc_submit_request(blkvsc_req,
+ blkvsc_request_completion);
+ if (ret == -1) {
+ pending = 1;
+ list_add_tail(&blkvsc_req->pend_entry,
+ &blkdev->pending_list);
+ }
+
+ DPRINT_DBG(BLKVSC_DRV, "submitted blkvsc_req %p "
+ "start_sect %lu sect_count %ld (%lu %ld) "
+ "ret %d\n", blkvsc_req,
+ (unsigned long)blkvsc_req->sector_start,
+ blkvsc_req->sector_count,
+ (unsigned long)start_sector,
+ num_sectors, ret);
+ }
+ }
+
+ return pending;
+}
+
+static void blkvsc_cmd_completion(struct hv_storvsc_request *request)
+{
+ struct blkvsc_request *blkvsc_req =
+ (struct blkvsc_request *)request->Context;
+ struct block_device_context *blkdev =
+ (struct block_device_context *)blkvsc_req->dev;
+ struct scsi_sense_hdr sense_hdr;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_cmd_completion() - req %p\n",
+ blkvsc_req);
+
+ blkdev->num_outstanding_reqs--;
+
+ if (blkvsc_req->request.Status)
+ if (scsi_normalize_sense(blkvsc_req->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, &sense_hdr))
+ scsi_print_sense_hdr("blkvsc", &sense_hdr);
+
+ blkvsc_req->cond = 1;
+ wake_up_interruptible(&blkvsc_req->wevent);
+}
+
+static void blkvsc_request_completion(struct hv_storvsc_request *request)
+{
+ struct blkvsc_request *blkvsc_req =
+ (struct blkvsc_request *)request->Context;
+ struct block_device_context *blkdev =
+ (struct block_device_context *)blkvsc_req->dev;
+ unsigned long flags;
+ struct blkvsc_request *comp_req, *tmp;
+
+ ASSERT(blkvsc_req->group);
+
+ DPRINT_DBG(BLKVSC_DRV, "blkdev %p blkvsc_req %p group %p type %s "
+ "sect_start %lu sect_count %ld len %d group outstd %d "
+ "total outstd %d\n",
+ blkdev, blkvsc_req, blkvsc_req->group,
+ (blkvsc_req->write) ? "WRITE" : "READ",
+ (unsigned long)blkvsc_req->sector_start,
+ blkvsc_req->sector_count,
+ blkvsc_req->request.DataBuffer.Length,
+ blkvsc_req->group->outstanding,
+ blkdev->num_outstanding_reqs);
+
+ spin_lock_irqsave(&blkdev->lock, flags);
+
+ blkdev->num_outstanding_reqs--;
+ blkvsc_req->group->outstanding--;
+
+ /*
+ * Only start processing when all the blkvsc_reqs are
+ * completed. This guarantees no out-of-order blkvsc_req
+ * completion when calling end_that_request_first()
+ */
+ if (blkvsc_req->group->outstanding == 0) {
+ list_for_each_entry_safe(comp_req, tmp,
+ &blkvsc_req->group->blkvsc_req_list,
+ req_entry) {
+ DPRINT_DBG(BLKVSC_DRV, "completing blkvsc_req %p "
+ "sect_start %lu sect_count %ld \n",
+ comp_req,
+ (unsigned long)comp_req->sector_start,
+ comp_req->sector_count);
+
+ list_del(&comp_req->req_entry);
+
+ if (!__blk_end_request(comp_req->req,
+ (!comp_req->request.Status ? 0 : -EIO),
+ comp_req->sector_count * blkdev->sector_size)) {
+ /*
+ * All the sectors have been xferred ie the
+ * request is done
+ */
+ DPRINT_DBG(BLKVSC_DRV, "req %p COMPLETED\n",
+ comp_req->req);
+ kmem_cache_free(blkdev->request_pool,
+ comp_req->group);
+ }
+
+ kmem_cache_free(blkdev->request_pool, comp_req);
+ }
+
+ if (!blkdev->shutting_down) {
+ blkvsc_do_pending_reqs(blkdev);
+ blk_start_queue(blkdev->gd->queue);
+ blkvsc_request(blkdev->gd->queue);
+ }
+ }
+
+ spin_unlock_irqrestore(&blkdev->lock, flags);
+}
+
+static int blkvsc_cancel_pending_reqs(struct block_device_context *blkdev)
+{
+ struct blkvsc_request *pend_req, *tmp;
+ struct blkvsc_request *comp_req, *tmp2;
+
+ int ret = 0;
+
+ DPRINT_DBG(BLKVSC_DRV, "blkvsc_cancel_pending_reqs()");
+
+ /* Flush the pending list first */
+ list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list,
+ pend_entry) {
+ /*
+ * The pend_req could be part of a partially completed
+ * request. If so, complete those req first until we
+ * hit the pend_req
+ */
+ list_for_each_entry_safe(comp_req, tmp2,
+ &pend_req->group->blkvsc_req_list,
+ req_entry) {
+ DPRINT_DBG(BLKVSC_DRV, "completing blkvsc_req %p "
+ "sect_start %lu sect_count %ld \n",
+ comp_req,
+ (unsigned long) comp_req->sector_start,
+ comp_req->sector_count);
+
+ if (comp_req == pend_req)
+ break;
+
+ list_del(&comp_req->req_entry);
+
+ if (comp_req->req) {
+ ret = __blk_end_request(comp_req->req,
+ (!comp_req->request.Status ? 0 : -EIO),
+ comp_req->sector_count *
+ blkdev->sector_size);
+ ASSERT(ret != 0);
+ }
+
+ kmem_cache_free(blkdev->request_pool, comp_req);
+ }
+
+ DPRINT_DBG(BLKVSC_DRV, "cancelling pending request - %p\n",
+ pend_req);
+
+ list_del(&pend_req->pend_entry);
+
+ list_del(&pend_req->req_entry);
+
+ if (comp_req->req) {
+ if (!__blk_end_request(pend_req->req, -EIO,
+ pend_req->sector_count *
+ blkdev->sector_size)) {
+ /*
+ * All the sectors have been xferred ie the
+ * request is done
+ */
+ DPRINT_DBG(BLKVSC_DRV,
+ "blkvsc_cancel_pending_reqs() - "
+ "req %p COMPLETED\n", pend_req->req);
+ kmem_cache_free(blkdev->request_pool,
+ pend_req->group);
+ }
+ }
+
+ kmem_cache_free(blkdev->request_pool, pend_req);
+ }
+
+ return ret;
+}
+
+static int blkvsc_do_pending_reqs(struct block_device_context *blkdev)
+{
+ struct blkvsc_request *pend_req, *tmp;
+ int ret = 0;
+
+ /* Flush the pending list first */
+ list_for_each_entry_safe(pend_req, tmp, &blkdev->pending_list,
+ pend_entry) {
+ DPRINT_DBG(BLKVSC_DRV, "working off pending_list - %p\n",
+ pend_req);
+
+ ret = blkvsc_submit_request(pend_req,
+ blkvsc_request_completion);
+ if (ret != 0)
+ break;
+ else
+ list_del(&pend_req->pend_entry);
+ }
+
+ return ret;
+}
+
+static void blkvsc_request(struct request_queue *queue)
+{
+ struct block_device_context *blkdev = NULL;
+ struct request *req;
+ int ret = 0;
+
+ DPRINT_DBG(BLKVSC_DRV, "- enter \n");
+ while ((req = blk_peek_request(queue)) != NULL) {
+ DPRINT_DBG(BLKVSC_DRV, "- req %p\n", req);
+
+ blkdev = req->rq_disk->private_data;
+ if (blkdev->shutting_down || !blk_fs_request(req) ||
+ blkdev->media_not_present) {
+ __blk_end_request_cur(req, 0);
+ continue;
+ }
+
+ ret = blkvsc_do_pending_reqs(blkdev);
+
+ if (ret != 0) {
+ DPRINT_DBG(BLKVSC_DRV,
+ "- stop queue - pending_list not empty\n");
+ blk_stop_queue(queue);
+ break;
+ }
+
+ blk_start_request(req);
+
+ ret = blkvsc_do_request(blkdev, req);
+ if (ret > 0) {
+ DPRINT_DBG(BLKVSC_DRV, "- stop queue - no room\n");
+ blk_stop_queue(queue);
+ break;
+ } else if (ret < 0) {
+ DPRINT_DBG(BLKVSC_DRV, "- stop queue - no mem\n");
+ blk_requeue_request(queue, req);
+ blk_stop_queue(queue);
+ break;
+ }
+ }
+}
+
+static int blkvsc_open(struct block_device *bdev, fmode_t mode)
+{
+ struct block_device_context *blkdev = bdev->bd_disk->private_data;
+
+ DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users,
+ blkdev->gd->disk_name);
+
+ spin_lock(&blkdev->lock);
+
+ if (!blkdev->users && blkdev->device_type == DVD_TYPE) {
+ spin_unlock(&blkdev->lock);
+ check_disk_change(bdev);
+ spin_lock(&blkdev->lock);
+ }
+
+ blkdev->users++;
+
+ spin_unlock(&blkdev->lock);
+ return 0;
+}
+
+static int blkvsc_release(struct gendisk *disk, fmode_t mode)
+{
+ struct block_device_context *blkdev = disk->private_data;
+
+ DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users,
+ blkdev->gd->disk_name);
+
+ spin_lock(&blkdev->lock);
+ if (blkdev->users == 1) {
+ spin_unlock(&blkdev->lock);
+ blkvsc_do_flush(blkdev);
+ spin_lock(&blkdev->lock);
+ }
+
+ blkdev->users--;
+
+ spin_unlock(&blkdev->lock);
+ return 0;
+}
+
+static int blkvsc_media_changed(struct gendisk *gd)
+{
+ DPRINT_DBG(BLKVSC_DRV, "- enter\n");
+ return 1;
+}
+
+static int blkvsc_revalidate_disk(struct gendisk *gd)
+{
+ struct block_device_context *blkdev = gd->private_data;
+
+ DPRINT_DBG(BLKVSC_DRV, "- enter\n");
+
+ if (blkdev->device_type == DVD_TYPE) {
+ blkvsc_do_read_capacity(blkdev);
+ set_capacity(blkdev->gd, blkdev->capacity *
+ (blkdev->sector_size/512));
+ blk_queue_logical_block_size(gd->queue, blkdev->sector_size);
+ }
+ return 0;
+}
+
+static int blkvsc_getgeo(struct block_device *bd, struct hd_geometry *hg)
+{
+ sector_t total_sectors = get_capacity(bd->bd_disk);
+ sector_t cylinder_times_heads = 0;
+ sector_t temp = 0;
+
+ int sectors_per_track = 0;
+ int heads = 0;
+ int cylinders = 0;
+ int rem = 0;
+
+ if (total_sectors > (65535 * 16 * 255))
+ total_sectors = (65535 * 16 * 255);
+
+ if (total_sectors >= (65535 * 16 * 63)) {
+ sectors_per_track = 255;
+ heads = 16;
+
+ cylinder_times_heads = total_sectors;
+ /* sector_div stores the quotient in cylinder_times_heads */
+ rem = sector_div(cylinder_times_heads, sectors_per_track);
+ } else {
+ sectors_per_track = 17;
+
+ cylinder_times_heads = total_sectors;
+ /* sector_div stores the quotient in cylinder_times_heads */
+ rem = sector_div(cylinder_times_heads, sectors_per_track);
+
+ temp = cylinder_times_heads + 1023;
+ /* sector_div stores the quotient in temp */
+ rem = sector_div(temp, 1024);
+
+ heads = temp;
+
+ if (heads < 4)
+ heads = 4;
+
+
+ if (cylinder_times_heads >= (heads * 1024) || (heads > 16)) {
+ sectors_per_track = 31;
+ heads = 16;
+
+ cylinder_times_heads = total_sectors;
+ /*
+ * sector_div stores the quotient in
+ * cylinder_times_heads
+ */
+ rem = sector_div(cylinder_times_heads,
+ sectors_per_track);
+ }
+
+ if (cylinder_times_heads >= (heads * 1024)) {
+ sectors_per_track = 63;
+ heads = 16;
+
+ cylinder_times_heads = total_sectors;
+ /*
+ * sector_div stores the quotient in
+ * cylinder_times_heads
+ */
+ rem = sector_div(cylinder_times_heads,
+ sectors_per_track);
+ }
+ }
+
+ temp = cylinder_times_heads;
+ /* sector_div stores the quotient in temp */
+ rem = sector_div(temp, heads);
+ cylinders = temp;
+
+ hg->heads = heads;
+ hg->sectors = sectors_per_track;
+ hg->cylinders = cylinders;
+
+ DPRINT_INFO(BLKVSC_DRV, "CHS (%d, %d, %d)", cylinders, heads,
+ sectors_per_track);
+
+ return 0;
+}
+
+static int blkvsc_ioctl(struct block_device *bd, fmode_t mode,
+ unsigned cmd, unsigned long argument)
+{
+/* struct block_device_context *blkdev = bd->bd_disk->private_data; */
+ int ret;
+
+ switch (cmd) {
+ /*
+ * TODO: I think there is certain format for HDIO_GET_IDENTITY rather
+ * than just a GUID. Commented it out for now.
+ */
+#if 0
+ case HDIO_GET_IDENTITY:
+ DPRINT_INFO(BLKVSC_DRV, "HDIO_GET_IDENTITY\n");
+ if (copy_to_user((void __user *)arg, blkdev->device_id,
+ blkdev->device_id_len))
+ ret = -EFAULT;
+ break;
+#endif
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int __init blkvsc_init(void)
+{
+ int ret;
+
+ ASSERT(sizeof(sector_t) == 8); /* Make sure CONFIG_LBD is set */
+
+ DPRINT_ENTER(BLKVSC_DRV);
+
+ DPRINT_INFO(BLKVSC_DRV, "Blkvsc initializing....");
+
+ ret = blkvsc_drv_init(BlkVscInitialize);
+
+ DPRINT_EXIT(BLKVSC_DRV);
+
+ return ret;
+}
+
+static void __exit blkvsc_exit(void)
+{
+ DPRINT_ENTER(BLKVSC_DRV);
+ blkvsc_drv_exit();
+ DPRINT_ENTER(BLKVSC_DRV);
+}
+
+MODULE_LICENSE("GPL");
+module_param(blkvsc_ringbuffer_size, int, S_IRUGO);
+module_init(blkvsc_init);
+module_exit(blkvsc_exit);
diff --git a/drivers/staging/hv/hv_api.h b/drivers/staging/hv/hv_api.h
new file mode 100644
index 000000000000..251e2d155331
--- /dev/null
+++ b/drivers/staging/hv/hv_api.h
@@ -0,0 +1,905 @@
+/*
+ *
+ * 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>
+ *
+ */
+#ifndef __HV_API_H
+#define __HV_API_H
+
+
+/* Status codes for hypervisor operations. */
+
+/*
+ * HV_STATUS_SUCCESS
+ * The specified hypercall succeeded
+ */
+#define HV_STATUS_SUCCESS ((u16)0x0000)
+
+/*
+ * HV_STATUS_INVALID_HYPERCALL_CODE
+ * The hypervisor does not support the operation because the specified
+ * hypercall code is not supported.
+ */
+#define HV_STATUS_INVALID_HYPERCALL_CODE ((u16)0x0002)
+
+/*
+ * HV_STATUS_INVALID_HYPERCALL_INPUT
+ * The hypervisor does not support the operation because the encoding for the
+ * hypercall input register is not supported.
+ */
+#define HV_STATUS_INVALID_HYPERCALL_INPUT ((u16)0x0003)
+
+/*
+ * HV_STATUS_INVALID_ALIGNMENT
+ * The hypervisor could not perform the operation beacuse a parameter has an
+ * invalid alignment.
+ */
+#define HV_STATUS_INVALID_ALIGNMENT ((u16)0x0004)
+
+/*
+ * HV_STATUS_INVALID_PARAMETER
+ * The hypervisor could not perform the operation beacuse an invalid parameter
+ * was specified.
+ */
+#define HV_STATUS_INVALID_PARAMETER ((u16)0x0005)
+
+/*
+ * HV_STATUS_ACCESS_DENIED
+ * Access to the specified object was denied.
+ */
+#define HV_STATUS_ACCESS_DENIED ((u16)0x0006)
+
+/*
+ * HV_STATUS_INVALID_PARTITION_STATE
+ * The hypervisor could not perform the operation because the partition is
+ * entering or in an invalid state.
+ */
+#define HV_STATUS_INVALID_PARTITION_STATE ((u16)0x0007)
+
+/*
+ * HV_STATUS_OPERATION_DENIED
+ * The operation is not allowed in the current state.
+ */
+#define HV_STATUS_OPERATION_DENIED ((u16)0x0008)
+
+/*
+ * HV_STATUS_UNKNOWN_PROPERTY
+ * The hypervisor does not recognize the specified partition property.
+ */
+#define HV_STATUS_UNKNOWN_PROPERTY ((u16)0x0009)
+
+/*
+ * HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE
+ * The specified value of a partition property is out of range or violates an
+ * invariant.
+ */
+#define HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE ((u16)0x000A)
+
+/*
+ * HV_STATUS_INSUFFICIENT_MEMORY
+ * There is not enough memory in the hypervisor pool to complete the operation.
+ */
+#define HV_STATUS_INSUFFICIENT_MEMORY ((u16)0x000B)
+
+/*
+ * HV_STATUS_PARTITION_TOO_DEEP
+ * The maximum partition depth has been exceeded for the partition hierarchy.
+ */
+#define HV_STATUS_PARTITION_TOO_DEEP ((u16)0x000C)
+
+/*
+ * HV_STATUS_INVALID_PARTITION_ID
+ * A partition with the specified partition Id does not exist.
+ */
+#define HV_STATUS_INVALID_PARTITION_ID ((u16)0x000D)
+
+/*
+ * HV_STATUS_INVALID_VP_INDEX
+ * The hypervisor could not perform the operation because the specified VP
+ * index is invalid.
+ */
+#define HV_STATUS_INVALID_VP_INDEX ((u16)0x000E)
+
+/*
+ * HV_STATUS_NOT_FOUND
+ * The iteration is complete; no addition items in the iteration could be
+ * found.
+ */
+#define HV_STATUS_NOT_FOUND ((u16)0x0010)
+
+/*
+ * HV_STATUS_INVALID_PORT_ID
+ * The hypervisor could not perform the operation because the specified port
+ * identifier is invalid.
+ */
+#define HV_STATUS_INVALID_PORT_ID ((u16)0x0011)
+
+/*
+ * HV_STATUS_INVALID_CONNECTION_ID
+ * The hypervisor could not perform the operation because the specified
+ * connection identifier is invalid.
+ */
+#define HV_STATUS_INVALID_CONNECTION_ID ((u16)0x0012)
+
+/*
+ * HV_STATUS_INSUFFICIENT_BUFFERS
+ * You did not supply enough message buffers to send a message.
+ */
+#define HV_STATUS_INSUFFICIENT_BUFFERS ((u16)0x0013)
+
+/*
+ * HV_STATUS_NOT_ACKNOWLEDGED
+ * The previous virtual interrupt has not been acknowledged.
+ */
+#define HV_STATUS_NOT_ACKNOWLEDGED ((u16)0x0014)
+
+/*
+ * HV_STATUS_INVALID_VP_STATE
+ * A virtual processor is not in the correct state for the performance of the
+ * indicated operation.
+ */
+#define HV_STATUS_INVALID_VP_STATE ((u16)0x0015)
+
+/*
+ * HV_STATUS_ACKNOWLEDGED
+ * The previous virtual interrupt has already been acknowledged.
+ */
+#define HV_STATUS_ACKNOWLEDGED ((u16)0x0016)
+
+/*
+ * HV_STATUS_INVALID_SAVE_RESTORE_STATE
+ * The indicated partition is not in a valid state for saving or restoring.
+ */
+#define HV_STATUS_INVALID_SAVE_RESTORE_STATE ((u16)0x0017)
+
+/*
+ * HV_STATUS_INVALID_SYNIC_STATE
+ * The hypervisor could not complete the operation because a required feature
+ * of the synthetic interrupt controller (SynIC) was disabled.
+ */
+#define HV_STATUS_INVALID_SYNIC_STATE ((u16)0x0018)
+
+/*
+ * HV_STATUS_OBJECT_IN_USE
+ * The hypervisor could not perform the operation because the object or value
+ * was either already in use or being used for a purpose that would not permit
+ * completing the operation.
+ */
+#define HV_STATUS_OBJECT_IN_USE ((u16)0x0019)
+
+/*
+ * HV_STATUS_INVALID_PROXIMITY_DOMAIN_INFO
+ * The proximity domain information is invalid.
+ */
+#define HV_STATUS_INVALID_PROXIMITY_DOMAIN_INFO ((u16)0x001A)
+
+/*
+ * HV_STATUS_NO_DATA
+ * An attempt to retrieve debugging data failed because none was available.
+ */
+#define HV_STATUS_NO_DATA ((u16)0x001B)
+
+/*
+ * HV_STATUS_INACTIVE
+ * The physical connection being used for debuggging has not recorded any
+ * receive activity since the last operation.
+ */
+#define HV_STATUS_INACTIVE ((u16)0x001C)
+
+/*
+ * HV_STATUS_NO_RESOURCES
+ * There are not enough resources to complete the operation.
+ */
+#define HV_STATUS_NO_RESOURCES ((u16)0x001D)
+
+/*
+ * HV_STATUS_FEATURE_UNAVAILABLE
+ * A hypervisor feature is not available to the user.
+ */
+#define HV_STATUS_FEATURE_UNAVAILABLE ((u16)0x001E)
+
+/*
+ * HV_STATUS_UNSUCCESSFUL
+ * {Operation Failed} The requested operation was unsuccessful.
+ */
+#define HV_STATUS_UNSUCCESSFUL ((u16)0x1001)
+
+/*
+ * HV_STATUS_INSUFFICIENT_BUFFER
+ * The specified buffer was too small to contain all of the requested data.
+ */
+#define HV_STATUS_INSUFFICIENT_BUFFER ((u16)0x1002)
+
+/*
+ * HV_STATUS_GPA_NOT_PRESENT
+ * The guest physical address is not currently associated with a system
+ * physical address.
+ */
+#define HV_STATUS_GPA_NOT_PRESENT ((u16)0x1003)
+
+/*
+ * HV_STATUS_GUEST_PAGE_FAULT
+ * The operation would have resulted in a page fault in the guest.
+ */
+#define HV_STATUS_GUEST_PAGE_FAULT ((u16)0x1004)
+
+/*
+ * HV_STATUS_RUNDOWN_DISABLED
+ * The operation cannot proceed as the rundown object was marked disabled.
+ */
+#define HV_STATUS_RUNDOWN_DISABLED ((u16)0x1005)
+
+/*
+ * HV_STATUS_KEY_ALREADY_EXISTS
+ * The entry cannot be added as another entry with the same key already exists.
+ */
+#define HV_STATUS_KEY_ALREADY_EXISTS ((u16)0x1006)
+
+/*
+ * HV_STATUS_GPA_INTERCEPT
+ * The operation resulted an intercept on a region of guest physical memory.
+ */
+#define HV_STATUS_GPA_INTERCEPT ((u16)0x1007)
+
+/*
+ * HV_STATUS_GUEST_GENERAL_PROTECTION_FAULT
+ * The operation would have resulted in a general protection fault in the
+ * guest.
+ */
+#define HV_STATUS_GUEST_GENERAL_PROTECTION_FAULT ((u16)0x1008)
+
+/*
+ * HV_STATUS_GUEST_STACK_FAULT
+ * The operation would have resulted in a stack fault in the guest.
+ */
+#define HV_STATUS_GUEST_STACK_FAULT ((u16)0x1009)
+
+/*
+ * HV_STATUS_GUEST_INVALID_OPCODE_FAULT
+ * The operation would have resulted in an invalid opcode fault in the guest.
+ */
+#define HV_STATUS_GUEST_INVALID_OPCODE_FAULT ((u16)0x100A)
+
+/*
+ * HV_STATUS_FINALIZE_INCOMPLETE
+ * The partition is not completely finalized.
+ */
+#define HV_STATUS_FINALIZE_INCOMPLETE ((u16)0x100B)
+
+/*
+ * HV_STATUS_GUEST_MACHINE_CHECK_ABORT
+ * The operation would have resulted in an machine check abort in the guest.
+ */
+#define HV_STATUS_GUEST_MACHINE_CHECK_ABORT ((u16)0x100C)
+
+/*
+ * HV_STATUS_ILLEGAL_OVERLAY_ACCESS
+ * An illegal access was attempted to an overlay page.
+ */
+#define HV_STATUS_ILLEGAL_OVERLAY_ACCESS ((u16)0x100D)
+
+/*
+ * HV_STATUS_INSUFFICIENT_SYSTEM_VA
+ * There is not enough system VA space available to satisfy the request,
+ */
+#define HV_STATUS_INSUFFICIENT_SYSTEM_VA ((u16)0x100E)
+
+/*
+ * HV_STATUS_VIRTUAL_ADDRESS_NOT_MAPPED
+ * The passed virtual address was not mapped in the hypervisor address space.
+ */
+#define HV_STATUS_VIRTUAL_ADDRESS_NOT_MAPPED ((u16)0x100F)
+
+/*
+ * HV_STATUS_NOT_IMPLEMENTED
+ * The requested operation is not implemented in this version of the
+ * hypervisor.
+ */
+#define HV_STATUS_NOT_IMPLEMENTED ((u16)0x1010)
+
+/*
+ * HV_STATUS_VMX_INSTRUCTION_FAILED
+ * The requested VMX instruction failed to complete succesfully.
+ */
+#define HV_STATUS_VMX_INSTRUCTION_FAILED ((u16)0x1011)
+
+/*
+ * HV_STATUS_VMX_INSTRUCTION_FAILED_WITH_STATUS
+ * The requested VMX instruction failed to complete succesfully indicating
+ * status.
+ */
+#define HV_STATUS_VMX_INSTRUCTION_FAILED_WITH_STATUS ((u16)0x1012)
+
+/*
+ * HV_STATUS_MSR_ACCESS_FAILED
+ * The requested access to the model specific register failed.
+ */
+#define HV_STATUS_MSR_ACCESS_FAILED ((u16)0x1013)
+
+/*
+ * HV_STATUS_CR_ACCESS_FAILED
+ * The requested access to the control register failed.
+ */
+#define HV_STATUS_CR_ACCESS_FAILED ((u16)0x1014)
+
+/*
+ * HV_STATUS_TIMEOUT
+ * The specified timeout expired before the operation completed.
+ */
+#define HV_STATUS_TIMEOUT ((u16)0x1016)
+
+/*
+ * HV_STATUS_MSR_INTERCEPT
+ * The requested access to the model specific register generated an intercept.
+ */
+#define HV_STATUS_MSR_INTERCEPT ((u16)0x1017)
+
+/*
+ * HV_STATUS_CPUID_INTERCEPT
+ * The CPUID instruction generated an intercept.
+ */
+#define HV_STATUS_CPUID_INTERCEPT ((u16)0x1018)
+
+/*
+ * HV_STATUS_REPEAT_INSTRUCTION
+ * The current instruction should be repeated and the instruction pointer not
+ * advanced.
+ */
+#define HV_STATUS_REPEAT_INSTRUCTION ((u16)0x1019)
+
+/*
+ * HV_STATUS_PAGE_PROTECTION_VIOLATION
+ * The current instruction should be repeated and the instruction pointer not
+ * advanced.
+ */
+#define HV_STATUS_PAGE_PROTECTION_VIOLATION ((u16)0x101A)
+
+/*
+ * HV_STATUS_PAGE_TABLE_INVALID
+ * The current instruction should be repeated and the instruction pointer not
+ * advanced.
+ */
+#define HV_STATUS_PAGE_TABLE_INVALID ((u16)0x101B)
+
+/*
+ * HV_STATUS_PAGE_NOT_PRESENT
+ * The current instruction should be repeated and the instruction pointer not
+ * advanced.
+ */
+#define HV_STATUS_PAGE_NOT_PRESENT ((u16)0x101C)
+
+/*
+ * HV_STATUS_IO_INTERCEPT
+ * The requested access to the I/O port generated an intercept.
+ */
+#define HV_STATUS_IO_INTERCEPT ((u16)0x101D)
+
+/*
+ * HV_STATUS_NOTHING_TO_DO
+ * There is nothing to do.
+ */
+#define HV_STATUS_NOTHING_TO_DO ((u16)0x101E)
+
+/*
+ * HV_STATUS_THREAD_TERMINATING
+ * The requested thread is terminating.
+ */
+#define HV_STATUS_THREAD_TERMINATING ((u16)0x101F)
+
+/*
+ * HV_STATUS_SECTION_ALREADY_CONSTRUCTED
+ * The specified section was already constructed.
+ */
+#define HV_STATUS_SECTION_ALREADY_CONSTRUCTED ((u16)0x1020)
+
+/* HV_STATUS_SECTION_NOT_ALREADY_CONSTRUCTED
+ * The specified section was not already constructed.
+ */
+#define HV_STATUS_SECTION_NOT_ALREADY_CONSTRUCTED ((u16)0x1021)
+
+/*
+ * HV_STATUS_PAGE_ALREADY_COMMITTED
+ * The specified virtual address was already backed by physical memory.
+ */
+#define HV_STATUS_PAGE_ALREADY_COMMITTED ((u16)0x1022)
+
+/*
+ * HV_STATUS_PAGE_NOT_ALREADY_COMMITTED
+ * The specified virtual address was not already backed by physical memory.
+ */
+#define HV_STATUS_PAGE_NOT_ALREADY_COMMITTED ((u16)0x1023)
+
+/*
+ * HV_STATUS_COMMITTED_PAGES_REMAIN
+ * Committed pages remain in the section.
+ */
+#define HV_STATUS_COMMITTED_PAGES_REMAIN ((u16)0x1024)
+
+/*
+ * HV_STATUS_NO_REMAINING_COMMITTED_PAGES
+ * No additional committed pages beyond the specified page exist in the
+ * section.
+ */
+#define HV_STATUS_NO_REMAINING_COMMITTED_PAGES ((u16)0x1025)
+
+/*
+ * HV_STATUS_INSUFFICIENT_COMPARTMENT_VA
+ * The VA space of the compartment is exhausted.
+ */
+#define HV_STATUS_INSUFFICIENT_COMPARTMENT_VA ((u16)0x1026)
+
+/*
+ * HV_STATUS_DEREF_SPA_LIST_FULL
+ * The SPA dereference list is full, and there are additional entries to be
+ * added to it.
+ */
+#define HV_STATUS_DEREF_SPA_LIST_FULL ((u16)0x1027)
+
+/*
+ * HV_STATUS_GPA_OUT_OF_RANGE
+ * The supplied GPA is out of range.
+ */
+#define HV_STATUS_GPA_OUT_OF_RANGE ((u16)0x1027)
+
+/*
+ * HV_STATUS_NONVOLATILE_XMM_STALE
+ * The XMM register that was being accessed is stale.
+ */
+#define HV_STATUS_NONVOLATILE_XMM_STALE ((u16)0x1028)
+
+/* HV_STATUS_UNSUPPORTED_PROCESSOR
+ * The hypervisor does not support the processors in this system.
+ */
+#define HV_STATUS_UNSUPPORTED_PROCESSOR ((u16)0x1029)
+
+/*
+ * HV_STATUS_INSUFFICIENT_CROM_SPACE
+ * Insufficient space existed for copying over the CROM contents.
+ */
+#define HV_STATUS_INSUFFICIENT_CROM_SPACE ((u16)0x2000)
+
+/*
+ * HV_STATUS_BAD_CROM_FORMAT
+ * The contents of the CROM failed validation attempts.
+ */
+#define HV_STATUS_BAD_CROM_FORMAT ((u16)0x2001)
+
+/*
+ * HV_STATUS_UNSUPPORTED_CROM_FORMAT
+ * The contents of the CROM contain contents the parser doesn't support.
+ */
+#define HV_STATUS_UNSUPPORTED_CROM_FORMAT ((u16)0x2002)
+
+/*
+ * HV_STATUS_UNSUPPORTED_CONTROLLER
+ * The register format of the OHCI controller specified for debugging is not
+ * supported.
+ */
+#define HV_STATUS_UNSUPPORTED_CONTROLLER ((u16)0x2003)
+
+/*
+ * HV_STATUS_CROM_TOO_LARGE
+ * The CROM contents were to large to copy over.
+ */
+#define HV_STATUS_CROM_TOO_LARGE ((u16)0x2004)
+
+/*
+ * HV_STATUS_CONTROLLER_IN_USE
+ * The OHCI controller specified for debugging cannot be used as it is already
+ * in use.
+ */
+#define HV_STATUS_CONTROLLER_IN_USE ((u16)0x2005)
+
+
+/*
+ * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
+ * is set by CPUID(HvCpuIdFunctionVersionAndFeatures).
+ */
+enum hv_cpuid_function {
+ HvCpuIdFunctionVersionAndFeatures = 0x00000001,
+ HvCpuIdFunctionHvVendorAndMaxFunction = 0x40000000,
+ HvCpuIdFunctionHvInterface = 0x40000001,
+
+ /*
+ * The remaining functions depend on the value of
+ * HvCpuIdFunctionInterface
+ */
+ HvCpuIdFunctionMsHvVersion = 0x40000002,
+ HvCpuIdFunctionMsHvFeatures = 0x40000003,
+ HvCpuIdFunctionMsHvEnlightenmentInformation = 0x40000004,
+ HvCpuIdFunctionMsHvImplementationLimits = 0x40000005,
+};
+
+/* Define the virtual APIC registers */
+#define HV_X64_MSR_EOI (0x40000070)
+#define HV_X64_MSR_ICR (0x40000071)
+#define HV_X64_MSR_TPR (0x40000072)
+#define HV_X64_MSR_APIC_ASSIST_PAGE (0x40000073)
+
+/* Define version of the synthetic interrupt controller. */
+#define HV_SYNIC_VERSION (1)
+
+/* Define synthetic interrupt controller model specific registers. */
+#define HV_X64_MSR_SCONTROL (0x40000080)
+#define HV_X64_MSR_SVERSION (0x40000081)
+#define HV_X64_MSR_SIEFP (0x40000082)
+#define HV_X64_MSR_SIMP (0x40000083)
+#define HV_X64_MSR_EOM (0x40000084)
+#define HV_X64_MSR_SINT0 (0x40000090)
+#define HV_X64_MSR_SINT1 (0x40000091)
+#define HV_X64_MSR_SINT2 (0x40000092)
+#define HV_X64_MSR_SINT3 (0x40000093)
+#define HV_X64_MSR_SINT4 (0x40000094)
+#define HV_X64_MSR_SINT5 (0x40000095)
+#define HV_X64_MSR_SINT6 (0x40000096)
+#define HV_X64_MSR_SINT7 (0x40000097)
+#define HV_X64_MSR_SINT8 (0x40000098)
+#define HV_X64_MSR_SINT9 (0x40000099)
+#define HV_X64_MSR_SINT10 (0x4000009A)
+#define HV_X64_MSR_SINT11 (0x4000009B)
+#define HV_X64_MSR_SINT12 (0x4000009C)
+#define HV_X64_MSR_SINT13 (0x4000009D)
+#define HV_X64_MSR_SINT14 (0x4000009E)
+#define HV_X64_MSR_SINT15 (0x4000009F)
+
+/* Define the expected SynIC version. */
+#define HV_SYNIC_VERSION_1 (0x1)
+
+/* Define synthetic interrupt controller message constants. */
+#define HV_MESSAGE_SIZE (256)
+#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240)
+#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30)
+#define HV_ANY_VP (0xFFFFFFFF)
+
+/* Define synthetic interrupt controller flag constants. */
+#define HV_EVENT_FLAGS_COUNT (256 * 8)
+#define HV_EVENT_FLAGS_BYTE_COUNT (256)
+#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32))
+
+/* Define hypervisor message types. */
+enum hv_message_type {
+ HvMessageTypeNone = 0x00000000,
+
+ /* Memory access messages. */
+ HvMessageTypeUnmappedGpa = 0x80000000,
+ HvMessageTypeGpaIntercept = 0x80000001,
+
+ /* Timer notification messages. */
+ HvMessageTimerExpired = 0x80000010,
+
+ /* Error messages. */
+ HvMessageTypeInvalidVpRegisterValue = 0x80000020,
+ HvMessageTypeUnrecoverableException = 0x80000021,
+ HvMessageTypeUnsupportedFeature = 0x80000022,
+
+ /* Trace buffer complete messages. */
+ HvMessageTypeEventLogBufferComplete = 0x80000040,
+
+ /* Platform-specific processor intercept messages. */
+ HvMessageTypeX64IoPortIntercept = 0x80010000,
+ HvMessageTypeX64MsrIntercept = 0x80010001,
+ HvMessageTypeX64CpuidIntercept = 0x80010002,
+ HvMessageTypeX64ExceptionIntercept = 0x80010003,
+ HvMessageTypeX64ApicEoi = 0x80010004,
+ HvMessageTypeX64LegacyFpError = 0x80010005
+};
+
+/* Define the number of synthetic interrupt sources. */
+#define HV_SYNIC_SINT_COUNT (16)
+#define HV_SYNIC_STIMER_COUNT (4)
+
+/* Define invalid partition identifier. */
+#define HV_PARTITION_ID_INVALID ((u64)0x0)
+
+/* Define connection identifier type. */
+union hv_connection_id {
+ u32 Asu32;
+ struct {
+ u32 Id:24;
+ u32 Reserved:8;
+ } u;
+};
+
+/* Define port identifier type. */
+union hv_port_id {
+ u32 Asu32;
+ struct {
+ u32 Id:24;
+ u32 Reserved:8;
+ } u ;
+};
+
+/* Define port type. */
+enum hv_port_type {
+ HvPortTypeMessage = 1,
+ HvPortTypeEvent = 2,
+ HvPortTypeMonitor = 3
+};
+
+/* Define port information structure. */
+struct hv_port_info {
+ enum hv_port_type PortType;
+ u32 Padding;
+ union {
+ struct {
+ u32 TargetSint;
+ u32 TargetVp;
+ u64 RsvdZ;
+ } MessagePortInfo;
+ struct {
+ u32 TargetSint;
+ u32 TargetVp;
+ u16 BaseFlagNumber;
+ u16 FlagCount;
+ u32 RsvdZ;
+ } EventPortInfo;
+ struct {
+ u64 MonitorAddress;
+ u64 RsvdZ;
+ } MonitorPortInfo;
+ };
+};
+
+struct hv_connection_info {
+ enum hv_port_type PortType;
+ u32 Padding;
+ union {
+ struct {
+ u64 RsvdZ;
+ } MessageConnectionInfo;
+ struct {
+ u64 RsvdZ;
+ } EventConnectionInfo;
+ struct {
+ u64 MonitorAddress;
+ } MonitorConnectionInfo;
+ };
+};
+
+/* Define synthetic interrupt controller message flags. */
+union hv_message_flags {
+ u8 Asu8;
+ struct {
+ u8 MessagePending:1;
+ u8 Reserved:7;
+ };
+};
+
+/* Define synthetic interrupt controller message header. */
+struct hv_message_header {
+ enum hv_message_type MessageType;
+ u8 PayloadSize;
+ union hv_message_flags MessageFlags;
+ u8 Reserved[2];
+ union {
+ u64 Sender;
+ union hv_port_id Port;
+ };
+};
+
+/* Define timer message payload structure. */
+struct hv_timer_message_payload {
+ u32 TimerIndex;
+ u32 Reserved;
+ u64 ExpirationTime; /* When the timer expired */
+ u64 DeliveryTime; /* When the message was delivered */
+};
+
+/* Define synthetic interrupt controller message format. */
+struct hv_message {
+ struct hv_message_header Header;
+ union {
+ u64 Payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
+ } u ;
+};
+
+/* Define the number of message buffers associated with each port. */
+#define HV_PORT_MESSAGE_BUFFER_COUNT (16)
+
+/* Define the synthetic interrupt message page layout. */
+struct hv_message_page {
+ struct hv_message SintMessage[HV_SYNIC_SINT_COUNT];
+};
+
+/* Define the synthetic interrupt controller event flags format. */
+union hv_synic_event_flags {
+ u8 Flags8[HV_EVENT_FLAGS_BYTE_COUNT];
+ u32 Flags32[HV_EVENT_FLAGS_DWORD_COUNT];
+};
+
+/* Define the synthetic interrupt flags page layout. */
+struct hv_synic_event_flags_page {
+ union hv_synic_event_flags SintEventFlags[HV_SYNIC_SINT_COUNT];
+};
+
+/* Define SynIC control register. */
+union hv_synic_scontrol {
+ u64 AsUINT64;
+ struct {
+ u64 Enable:1;
+ u64 Reserved:63;
+ };
+};
+
+/* Define synthetic interrupt source. */
+union hv_synic_sint {
+ u64 AsUINT64;
+ struct {
+ u64 Vector:8;
+ u64 Reserved1:8;
+ u64 Masked:1;
+ u64 AutoEoi:1;
+ u64 Reserved2:46;
+ };
+};
+
+/* Define the format of the SIMP register */
+union hv_synic_simp {
+ u64 AsUINT64;
+ struct {
+ u64 SimpEnabled:1;
+ u64 Preserved:11;
+ u64 BaseSimpGpa:52;
+ };
+};
+
+/* Define the format of the SIEFP register */
+union hv_synic_siefp {
+ u64 AsUINT64;
+ struct {
+ u64 SiefpEnabled:1;
+ u64 Preserved:11;
+ u64 BaseSiefpGpa:52;
+ };
+};
+
+/* Definitions for the monitored notification facility */
+union hv_monitor_trigger_group {
+ u64 AsUINT64;
+ struct {
+ u32 Pending;
+ u32 Armed;
+ };
+};
+
+struct hv_monitor_parameter {
+ union hv_connection_id ConnectionId;
+ u16 FlagNumber;
+ u16 RsvdZ;
+};
+
+union hv_monitor_trigger_state {
+ u32 Asu32;
+
+ struct {
+ u32 GroupEnable:4;
+ u32 RsvdZ:28;
+ };
+};
+
+/* struct hv_monitor_page Layout */
+/* ------------------------------------------------------ */
+/* | 0 | TriggerState (4 bytes) | Rsvd1 (4 bytes) | */
+/* | 8 | TriggerGroup[0] | */
+/* | 10 | TriggerGroup[1] | */
+/* | 18 | TriggerGroup[2] | */
+/* | 20 | TriggerGroup[3] | */
+/* | 28 | Rsvd2[0] | */
+/* | 30 | Rsvd2[1] | */
+/* | 38 | Rsvd2[2] | */
+/* | 40 | NextCheckTime[0][0] | NextCheckTime[0][1] | */
+/* | ... | */
+/* | 240 | Latency[0][0..3] | */
+/* | 340 | Rsvz3[0] | */
+/* | 440 | Parameter[0][0] | */
+/* | 448 | Parameter[0][1] | */
+/* | ... | */
+/* | 840 | Rsvd4[0] | */
+/* ------------------------------------------------------ */
+struct hv_monitor_page {
+ union hv_monitor_trigger_state TriggerState;
+ u32 RsvdZ1;
+
+ union hv_monitor_trigger_group TriggerGroup[4];
+ u64 RsvdZ2[3];
+
+ s32 NextCheckTime[4][32];
+
+ u16 Latency[4][32];
+ u64 RsvdZ3[32];
+
+ struct hv_monitor_parameter Parameter[4][32];
+
+ u8 RsvdZ4[1984];
+};
+
+/* Declare the various hypercall operations. */
+enum hv_call_code {
+ HvCallPostMessage = 0x005c,
+ HvCallSignalEvent = 0x005d,
+};
+
+/* Definition of the HvPostMessage hypercall input structure. */
+struct hv_input_post_message {
+ union hv_connection_id ConnectionId;
+ u32 Reserved;
+ enum hv_message_type MessageType;
+ u32 PayloadSize;
+ u64 Payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
+};
+
+/* Definition of the HvSignalEvent hypercall input structure. */
+struct hv_input_signal_event {
+ union hv_connection_id ConnectionId;
+ u16 FlagNumber;
+ u16 RsvdZ;
+};
+
+/*
+ * Versioning definitions used for guests reporting themselves to the
+ * hypervisor, and visa versa.
+ */
+
+/* Version info reported by guest OS's */
+enum hv_guest_os_vendor {
+ HvGuestOsVendorMicrosoft = 0x0001
+};
+
+enum hv_guest_os_microsoft_ids {
+ HvGuestOsMicrosoftUndefined = 0x00,
+ HvGuestOsMicrosoftMSDOS = 0x01,
+ HvGuestOsMicrosoftWindows3x = 0x02,
+ HvGuestOsMicrosoftWindows9x = 0x03,
+ HvGuestOsMicrosoftWindowsNT = 0x04,
+ HvGuestOsMicrosoftWindowsCE = 0x05
+};
+
+/*
+ * Declare the MSR used to identify the guest OS.
+ */
+#define HV_X64_MSR_GUEST_OS_ID 0x40000000
+
+union hv_x64_msr_guest_os_id_contents {
+ u64 AsUINT64;
+ struct {
+ u64 BuildNumber:16;
+ u64 ServiceVersion:8; /* Service Pack, etc. */
+ u64 MinorVersion:8;
+ u64 MajorVersion:8;
+ u64 OsId:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */
+ u64 VendorId:16; /* enum hv_guest_os_vendor */
+ };
+};
+
+/*
+ * Declare the MSR used to setup pages used to communicate with the hypervisor.
+ */
+#define HV_X64_MSR_HYPERCALL 0x40000001
+
+union hv_x64_msr_hypercall_contents {
+ u64 AsUINT64;
+ struct {
+ u64 Enable:1;
+ u64 Reserved:11;
+ u64 GuestPhysicalAddress:52;
+ };
+};
+
+#endif
diff --git a/drivers/staging/hv/logging.h b/drivers/staging/hv/logging.h
new file mode 100644
index 000000000000..9e55617bd670
--- /dev/null
+++ b/drivers/staging/hv/logging.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _LOGGING_H_
+#define _LOGGING_H_
+
+/* #include <linux/init.h> */
+/* #include <linux/module.h> */
+
+
+#define VMBUS 0x0001
+#define STORVSC 0x0002
+#define NETVSC 0x0004
+#define INPUTVSC 0x0008
+#define BLKVSC 0x0010
+#define VMBUS_DRV 0x0100
+#define STORVSC_DRV 0x0200
+#define NETVSC_DRV 0x0400
+#define INPUTVSC_DRV 0x0800
+#define BLKVSC_DRV 0x1000
+
+#define ALL_MODULES (VMBUS |\
+ STORVSC |\
+ NETVSC |\
+ INPUTVSC |\
+ BLKVSC |\
+ VMBUS_DRV |\
+ STORVSC_DRV |\
+ NETVSC_DRV |\
+ INPUTVSC_DRV|\
+ BLKVSC_DRV)
+
+/* Logging Level */
+#define ERROR_LVL 3
+#define WARNING_LVL 4
+#define INFO_LVL 6
+#define DEBUG_LVL 7
+#define DEBUG_LVL_ENTEREXIT 8
+#define DEBUG_RING_LVL 9
+
+extern unsigned int vmbus_loglevel;
+
+#define ASSERT(expr) \
+ if (!(expr)) { \
+ printk(KERN_CRIT "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr, __FILE__, __func__, __LINE__); \
+ __asm__ __volatile__("int3"); \
+ }
+
+#define DPRINT(mod, lvl, fmt, args...) do {\
+ if ((mod & (HIWORD(vmbus_loglevel))) && \
+ (lvl <= LOWORD(vmbus_loglevel))) \
+ printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\
+ } while (0)
+
+#define DPRINT_DBG(mod, fmt, args...) do {\
+ if ((mod & (HIWORD(vmbus_loglevel))) && \
+ (DEBUG_LVL <= LOWORD(vmbus_loglevel))) \
+ printk(KERN_DEBUG #mod": %s() " fmt "\n", __func__, ## args);\
+ } while (0)
+
+#define DPRINT_INFO(mod, fmt, args...) do {\
+ if ((mod & (HIWORD(vmbus_loglevel))) && \
+ (INFO_LVL <= LOWORD(vmbus_loglevel))) \
+ printk(KERN_INFO #mod": " fmt "\n", ## args);\
+ } while (0)
+
+#define DPRINT_WARN(mod, fmt, args...) do {\
+ if ((mod & (HIWORD(vmbus_loglevel))) && \
+ (WARNING_LVL <= LOWORD(vmbus_loglevel))) \
+ printk(KERN_WARNING #mod": WARNING! " fmt "\n", ## args);\
+ } while (0)
+
+#define DPRINT_ERR(mod, fmt, args...) do {\
+ if ((mod & (HIWORD(vmbus_loglevel))) && \
+ (ERROR_LVL <= LOWORD(vmbus_loglevel))) \
+ printk(KERN_ERR #mod": %s() ERROR!! " fmt "\n", \
+ __func__, ## args);\
+ } while (0)
+
+#ifdef DEBUG
+#define DPRINT_ENTER(mod) do {\
+ if ((mod & (HIWORD(vmbus_loglevel))) && \
+ (DEBUG_LVL_ENTEREXIT <= LOWORD(vmbus_loglevel))) \
+ printk(KERN_DEBUG "["#mod"]: %s() enter\n", __func__);\
+ } while (0)
+
+#define DPRINT_EXIT(mod) do {\
+ if ((mod & (HIWORD(vmbus_loglevel))) && \
+ (DEBUG_LVL_ENTEREXIT <= LOWORD(vmbus_loglevel))) \
+ printk(KERN_DEBUG "["#mod"]: %s() exit\n", __func__);\
+ } while (0)
+#else
+#define DPRINT_ENTER(mod)
+#define DPRINT_EXIT(mod)
+#endif
+
+#endif /* _LOGGING_H_ */
diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c
new file mode 100644
index 000000000000..3192d50f7251
--- /dev/null
+++ b/drivers/staging/hv/netvsc_drv.c
@@ -0,0 +1,618 @@
+/*
+ * 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:
+ * Hank Janssen <hjanssen@microsoft.com>
+ */
+#include <linux/init.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 <net/arp.h>
+#include <net/route.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+#include "osd.h"
+#include "logging.h"
+#include "vmbus.h"
+#include "NetVscApi.h"
+
+MODULE_LICENSE("GPL");
+
+struct net_device_context {
+ /* point back to our device context */
+ struct device_context *device_ctx;
+ struct net_device_stats stats;
+};
+
+struct netvsc_driver_context {
+ /* !! These must be the first 2 fields !! */
+ /* Which is a bug FIXME! */
+ struct driver_context drv_ctx;
+ struct netvsc_driver drv_obj;
+};
+
+static int netvsc_ringbuffer_size = NETVSC_DEVICE_RING_BUFFER_SIZE;
+
+/* The one and only one */
+static struct netvsc_driver_context g_netvsc_drv;
+
+static struct net_device_stats *netvsc_get_stats(struct net_device *net)
+{
+ struct net_device_context *net_device_ctx = netdev_priv(net);
+
+ return &net_device_ctx->stats;
+}
+
+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 driver_context *driver_ctx =
+ driver_to_driver_context(net_device_ctx->device_ctx->device.driver);
+ struct netvsc_driver_context *net_drv_ctx =
+ (struct netvsc_driver_context *)driver_ctx;
+ struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj;
+ struct hv_device *device_obj = &net_device_ctx->device_ctx->device_obj;
+ int ret = 0;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ if (netif_carrier_ok(net)) {
+ memset(&net_device_ctx->stats, 0,
+ sizeof(struct net_device_stats));
+
+ /* Open up the device */
+ ret = net_drv_obj->OnOpen(device_obj);
+ if (ret != 0) {
+ DPRINT_ERR(NETVSC_DRV,
+ "unable to open device (ret %d).", ret);
+ return ret;
+ }
+
+ netif_start_queue(net);
+ } else {
+ DPRINT_ERR(NETVSC_DRV, "unable to open device...link is down.");
+ }
+
+ DPRINT_EXIT(NETVSC_DRV);
+ return ret;
+}
+
+static int netvsc_close(struct net_device *net)
+{
+ struct net_device_context *net_device_ctx = netdev_priv(net);
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(net_device_ctx->device_ctx->device.driver);
+ struct netvsc_driver_context *net_drv_ctx =
+ (struct netvsc_driver_context *)driver_ctx;
+ struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj;
+ struct hv_device *device_obj = &net_device_ctx->device_ctx->device_obj;
+ int ret;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ netif_stop_queue(net);
+
+ ret = net_drv_obj->OnClose(device_obj);
+ if (ret != 0)
+ DPRINT_ERR(NETVSC_DRV, "unable to close device (ret %d).", ret);
+
+ DPRINT_EXIT(NETVSC_DRV);
+
+ 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.SendCompletionTid;
+ struct net_device *net;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ kfree(packet);
+
+ if (skb) {
+ net = skb->dev;
+ dev_kfree_skb_any(skb);
+
+ if (netif_queue_stopped(net)) {
+ DPRINT_INFO(NETVSC_DRV, "net device (%p) waking up...",
+ net);
+
+ netif_wake_queue(net);
+ }
+ }
+
+ DPRINT_EXIT(NETVSC_DRV);
+}
+
+static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct net_device_context *net_device_ctx = netdev_priv(net);
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(net_device_ctx->device_ctx->device.driver);
+ struct netvsc_driver_context *net_drv_ctx =
+ (struct netvsc_driver_context *)driver_ctx;
+ struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj;
+ struct hv_netvsc_packet *packet;
+ int i;
+ int ret;
+ int num_frags;
+ int retries = 0;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ /* Support only 1 chain of frags */
+ ASSERT(skb_shinfo(skb)->frag_list == NULL);
+ ASSERT(skb->dev == net);
+
+ DPRINT_DBG(NETVSC_DRV, "xmit packet - len %d data_len %d",
+ skb->len, skb->data_len);
+
+ /* Add 1 for skb->data and any additional ones requested */
+ num_frags = skb_shinfo(skb)->nr_frags + 1 +
+ net_drv_obj->AdditionalRequestPageBufferCount;
+
+ /* Allocate a netvsc packet based on # of frags. */
+ packet = kzalloc(sizeof(struct hv_netvsc_packet) +
+ (num_frags * sizeof(struct hv_page_buffer)) +
+ net_drv_obj->RequestExtSize, GFP_ATOMIC);
+ if (!packet) {
+ DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet");
+ return -1;
+ }
+
+ packet->Extension = (void *)(unsigned long)packet +
+ sizeof(struct hv_netvsc_packet) +
+ (num_frags * sizeof(struct hv_page_buffer));
+
+ /* Setup the rndis header */
+ packet->PageBufferCount = num_frags;
+
+ /* TODO: Flush all write buffers/ memory fence ??? */
+ /* wmb(); */
+
+ /* Initialize it from the skb */
+ ASSERT(skb->data);
+ packet->TotalDataBufferLength = skb->len;
+
+ /*
+ * Start filling in the page buffers starting at
+ * AdditionalRequestPageBufferCount offset
+ */
+ packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
+ packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Offset = (unsigned long)skb->data & (PAGE_SIZE - 1);
+ packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Length = skb->len - skb->data_len;
+
+ ASSERT((skb->len - skb->data_len) <= PAGE_SIZE);
+
+ for (i = net_drv_obj->AdditionalRequestPageBufferCount + 1;
+ i < num_frags; i++) {
+ packet->PageBuffers[i].Pfn =
+ page_to_pfn(skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page);
+ packet->PageBuffers[i].Offset =
+ skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page_offset;
+ packet->PageBuffers[i].Length =
+ skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].size;
+ }
+
+ /* Set the completion routine */
+ packet->Completion.Send.OnSendCompletion = netvsc_xmit_completion;
+ packet->Completion.Send.SendCompletionContext = packet;
+ packet->Completion.Send.SendCompletionTid = (unsigned long)skb;
+
+retry_send:
+ ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj,
+ packet);
+
+ if (ret == 0) {
+ ret = NETDEV_TX_OK;
+ net_device_ctx->stats.tx_bytes += skb->len;
+ net_device_ctx->stats.tx_packets++;
+ } else {
+ retries++;
+ if (retries < 4) {
+ DPRINT_ERR(NETVSC_DRV, "unable to send..."
+ "retrying %d...", retries);
+ udelay(100);
+ goto retry_send;
+ }
+
+ /* no more room or we are shutting down */
+ DPRINT_ERR(NETVSC_DRV, "unable to send (%d)..."
+ "marking net device (%p) busy", ret, net);
+ DPRINT_INFO(NETVSC_DRV, "net device (%p) stopping", net);
+
+ ret = NETDEV_TX_BUSY;
+ net_device_ctx->stats.tx_dropped++;
+
+ netif_stop_queue(net);
+
+ /*
+ * Null it since the caller will free it instead of the
+ * completion routine
+ */
+ packet->Completion.Send.SendCompletionTid = 0;
+
+ /*
+ * Release the resources since we will not get any send
+ * completion
+ */
+ netvsc_xmit_completion((void *)packet);
+ }
+
+ DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu",
+ net_device_ctx->stats.tx_packets,
+ net_device_ctx->stats.tx_bytes);
+
+ DPRINT_EXIT(NETVSC_DRV);
+ return ret;
+}
+
+/**
+ * netvsc_linkstatus_callback - Link up/down notification
+ */
+static void netvsc_linkstatus_callback(struct hv_device *device_obj,
+ unsigned int status)
+{
+ struct device_context *device_ctx = to_device_context(device_obj);
+ struct net_device *net = dev_get_drvdata(&device_ctx->device);
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ if (!net) {
+ DPRINT_ERR(NETVSC_DRV, "got link status but net device "
+ "not initialized yet");
+ return;
+ }
+
+ if (status == 1) {
+ netif_carrier_on(net);
+ netif_wake_queue(net);
+ } else {
+ netif_carrier_off(net);
+ netif_stop_queue(net);
+ }
+ DPRINT_EXIT(NETVSC_DRV);
+}
+
+/**
+ * netvsc_recv_callback - Callback when we receive a packet from the "wire" on the specified device.
+ */
+static int netvsc_recv_callback(struct hv_device *device_obj,
+ struct hv_netvsc_packet *packet)
+{
+ struct device_context *device_ctx = to_device_context(device_obj);
+ struct net_device *net = dev_get_drvdata(&device_ctx->device);
+ struct net_device_context *net_device_ctx;
+ struct sk_buff *skb;
+ void *data;
+ int ret;
+ int i;
+ unsigned long flags;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ if (!net) {
+ DPRINT_ERR(NETVSC_DRV, "got receive callback but net device "
+ "not initialized yet");
+ return 0;
+ }
+
+ net_device_ctx = netdev_priv(net);
+
+ /* Allocate a skb - TODO preallocate this */
+ /* Pad 2-bytes to align IP header to 16 bytes */
+ skb = dev_alloc_skb(packet->TotalDataBufferLength + 2);
+ ASSERT(skb);
+ skb_reserve(skb, 2);
+ skb->dev = net;
+
+ /* 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->PageBufferCount; i++) {
+ data = kmap_atomic(pfn_to_page(packet->PageBuffers[i].Pfn),
+ KM_IRQ1);
+ data = (void *)(unsigned long)data +
+ packet->PageBuffers[i].Offset;
+
+ memcpy(skb_put(skb, packet->PageBuffers[i].Length), data,
+ packet->PageBuffers[i].Length);
+
+ kunmap_atomic((void *)((unsigned long)data -
+ packet->PageBuffers[i].Offset), KM_IRQ1);
+ }
+
+ local_irq_restore(flags);
+
+ skb->protocol = eth_type_trans(skb, net);
+
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /*
+ * Pass the skb back up. Network stack will deallocate the skb when it
+ * is done
+ */
+ ret = netif_rx(skb);
+
+ switch (ret) {
+ case NET_RX_DROP:
+ net_device_ctx->stats.rx_dropped++;
+ break;
+ default:
+ net_device_ctx->stats.rx_packets++;
+ net_device_ctx->stats.rx_bytes += skb->len;
+ break;
+
+ }
+ DPRINT_DBG(NETVSC_DRV, "# of recvs %lu total size %lu",
+ net_device_ctx->stats.rx_packets,
+ net_device_ctx->stats.rx_bytes);
+
+ DPRINT_EXIT(NETVSC_DRV);
+
+ return 0;
+}
+
+static const struct net_device_ops device_ops = {
+ .ndo_open = netvsc_open,
+ .ndo_stop = netvsc_close,
+ .ndo_start_xmit = netvsc_start_xmit,
+ .ndo_get_stats = netvsc_get_stats,
+ .ndo_set_multicast_list = netvsc_set_multicast_list,
+};
+
+static int netvsc_probe(struct device *device)
+{
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device->driver);
+ struct netvsc_driver_context *net_drv_ctx =
+ (struct netvsc_driver_context *)driver_ctx;
+ struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj;
+ struct device_context *device_ctx = device_to_device_context(device);
+ struct hv_device *device_obj = &device_ctx->device_obj;
+ struct net_device *net = NULL;
+ struct net_device_context *net_device_ctx;
+ struct netvsc_device_info device_info;
+ int ret;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ if (!net_drv_obj->Base.OnDeviceAdd)
+ return -1;
+
+ net = alloc_netdev(sizeof(struct net_device_context), "seth%d",
+ ether_setup);
+ if (!net)
+ return -1;
+
+ /* Set initial state */
+ netif_carrier_off(net);
+ netif_stop_queue(net);
+
+ net_device_ctx = netdev_priv(net);
+ net_device_ctx->device_ctx = device_ctx;
+ dev_set_drvdata(device, net);
+
+ /* Notify the netvsc driver of the new device */
+ ret = net_drv_obj->Base.OnDeviceAdd(device_obj, &device_info);
+ if (ret != 0) {
+ free_netdev(net);
+ dev_set_drvdata(device, NULL);
+
+ DPRINT_ERR(NETVSC_DRV, "unable to add netvsc device (ret %d)",
+ ret);
+ return ret;
+ }
+
+ /*
+ * If carrier is still off ie we did not get a link status callback,
+ * update it if necessary
+ */
+ /*
+ * FIXME: We should use a atomic or test/set instead to avoid getting
+ * out of sync with the device's link status
+ */
+ if (!netif_carrier_ok(net))
+ if (!device_info.LinkState)
+ netif_carrier_on(net);
+
+ memcpy(net->dev_addr, device_info.MacAddr, ETH_ALEN);
+
+ net->netdev_ops = &device_ops;
+
+ SET_NETDEV_DEV(net, device);
+
+ ret = register_netdev(net);
+ if (ret != 0) {
+ /* Remove the device and release the resource */
+ net_drv_obj->Base.OnDeviceRemove(device_obj);
+ free_netdev(net);
+ }
+
+ DPRINT_EXIT(NETVSC_DRV);
+ return ret;
+}
+
+static int netvsc_remove(struct device *device)
+{
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device->driver);
+ struct netvsc_driver_context *net_drv_ctx =
+ (struct netvsc_driver_context *)driver_ctx;
+ struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj;
+ struct device_context *device_ctx = device_to_device_context(device);
+ struct net_device *net = dev_get_drvdata(&device_ctx->device);
+ struct hv_device *device_obj = &device_ctx->device_obj;
+ int ret;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ if (net == NULL) {
+ DPRINT_INFO(NETVSC, "no net device to remove");
+ DPRINT_EXIT(NETVSC_DRV);
+ return 0;
+ }
+
+ if (!net_drv_obj->Base.OnDeviceRemove) {
+ DPRINT_EXIT(NETVSC_DRV);
+ return -1;
+ }
+
+ /* Stop outbound asap */
+ netif_stop_queue(net);
+ /* netif_carrier_off(net); */
+
+ unregister_netdev(net);
+
+ /*
+ * Call to the vsc driver to let it know that the device is being
+ * removed
+ */
+ ret = net_drv_obj->Base.OnDeviceRemove(device_obj);
+ if (ret != 0) {
+ /* TODO: */
+ DPRINT_ERR(NETVSC, "unable to remove vsc device (ret %d)", ret);
+ }
+
+ free_netdev(net);
+ DPRINT_EXIT(NETVSC_DRV);
+ return ret;
+}
+
+static int netvsc_drv_exit_cb(struct device *dev, void *data)
+{
+ struct device **curr = (struct device **)data;
+
+ *curr = dev;
+ /* stop iterating */
+ return 1;
+}
+
+static void netvsc_drv_exit(void)
+{
+ struct netvsc_driver *netvsc_drv_obj = &g_netvsc_drv.drv_obj;
+ struct driver_context *drv_ctx = &g_netvsc_drv.drv_ctx;
+ struct device *current_dev;
+ int ret;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ while (1) {
+ current_dev = NULL;
+
+ /* Get the device */
+ ret = driver_for_each_device(&drv_ctx->driver, NULL,
+ &current_dev, netvsc_drv_exit_cb);
+ if (ret)
+ DPRINT_WARN(NETVSC_DRV,
+ "driver_for_each_device returned %d", ret);
+
+ if (current_dev == NULL)
+ break;
+
+ /* Initiate removal from the top-down */
+ DPRINT_INFO(NETVSC_DRV, "unregistering device (%p)...",
+ current_dev);
+
+ device_unregister(current_dev);
+ }
+
+ if (netvsc_drv_obj->Base.OnCleanup)
+ netvsc_drv_obj->Base.OnCleanup(&netvsc_drv_obj->Base);
+
+ vmbus_child_driver_unregister(drv_ctx);
+
+ DPRINT_EXIT(NETVSC_DRV);
+
+ return;
+}
+
+static int netvsc_drv_init(int (*drv_init)(struct hv_driver *drv))
+{
+ struct netvsc_driver *net_drv_obj = &g_netvsc_drv.drv_obj;
+ struct driver_context *drv_ctx = &g_netvsc_drv.drv_ctx;
+ int ret;
+
+ DPRINT_ENTER(NETVSC_DRV);
+
+ vmbus_get_interface(&net_drv_obj->Base.VmbusChannelInterface);
+
+ net_drv_obj->RingBufferSize = netvsc_ringbuffer_size;
+ net_drv_obj->OnReceiveCallback = netvsc_recv_callback;
+ net_drv_obj->OnLinkStatusChanged = netvsc_linkstatus_callback;
+
+ /* Callback to client driver to complete the initialization */
+ drv_init(&net_drv_obj->Base);
+
+ drv_ctx->driver.name = net_drv_obj->Base.name;
+ memcpy(&drv_ctx->class_id, &net_drv_obj->Base.deviceType,
+ sizeof(struct hv_guid));
+
+ drv_ctx->probe = netvsc_probe;
+ drv_ctx->remove = netvsc_remove;
+
+ /* The driver belongs to vmbus */
+ ret = vmbus_child_driver_register(drv_ctx);
+
+ DPRINT_EXIT(NETVSC_DRV);
+
+ return ret;
+}
+
+static int __init netvsc_init(void)
+{
+ int ret;
+
+ DPRINT_ENTER(NETVSC_DRV);
+ DPRINT_INFO(NETVSC_DRV, "Netvsc initializing....");
+
+ ret = netvsc_drv_init(NetVscInitialize);
+
+ DPRINT_EXIT(NETVSC_DRV);
+
+ return ret;
+}
+
+static void __exit netvsc_exit(void)
+{
+ DPRINT_ENTER(NETVSC_DRV);
+ netvsc_drv_exit();
+ DPRINT_EXIT(NETVSC_DRV);
+}
+
+module_param(netvsc_ringbuffer_size, int, S_IRUGO);
+
+module_init(netvsc_init);
+module_exit(netvsc_exit);
diff --git a/drivers/staging/hv/osd.c b/drivers/staging/hv/osd.c
new file mode 100644
index 000000000000..8fe543bd9910
--- /dev/null
+++ b/drivers/staging/hv/osd.c
@@ -0,0 +1,156 @@
+/*
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include "osd.h"
+
+struct osd_callback_struct {
+ struct work_struct work;
+ void (*callback)(void *);
+ void *data;
+};
+
+void *osd_VirtualAllocExec(unsigned int size)
+{
+#ifdef __x86_64__
+ return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL_EXEC);
+#else
+ return __vmalloc(size, GFP_KERNEL,
+ __pgprot(__PAGE_KERNEL & (~_PAGE_NX)));
+#endif
+}
+
+void *osd_PageAlloc(unsigned int count)
+{
+ void *p;
+
+ p = (void *)__get_free_pages(GFP_KERNEL, get_order(count * PAGE_SIZE));
+ if (p)
+ memset(p, 0, count * PAGE_SIZE);
+ return p;
+
+ /* struct page* page = alloc_page(GFP_KERNEL|__GFP_ZERO); */
+ /* void *p; */
+
+ /* BUGBUG: We need to use kmap in case we are in HIMEM region */
+ /* p = page_address(page); */
+ /* if (p) memset(p, 0, PAGE_SIZE); */
+ /* return p; */
+}
+EXPORT_SYMBOL_GPL(osd_PageAlloc);
+
+void osd_PageFree(void *page, unsigned int count)
+{
+ free_pages((unsigned long)page, get_order(count * PAGE_SIZE));
+ /*struct page* p = virt_to_page(page);
+ __free_page(p);*/
+}
+EXPORT_SYMBOL_GPL(osd_PageFree);
+
+struct osd_waitevent *osd_WaitEventCreate(void)
+{
+ struct osd_waitevent *wait = kmalloc(sizeof(struct osd_waitevent),
+ GFP_KERNEL);
+ if (!wait)
+ return NULL;
+
+ wait->condition = 0;
+ init_waitqueue_head(&wait->event);
+ return wait;
+}
+EXPORT_SYMBOL_GPL(osd_WaitEventCreate);
+
+void osd_WaitEventSet(struct osd_waitevent *waitEvent)
+{
+ waitEvent->condition = 1;
+ wake_up_interruptible(&waitEvent->event);
+}
+EXPORT_SYMBOL_GPL(osd_WaitEventSet);
+
+int osd_WaitEventWait(struct osd_waitevent *waitEvent)
+{
+ int ret = 0;
+
+ ret = wait_event_interruptible(waitEvent->event,
+ waitEvent->condition);
+ waitEvent->condition = 0;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(osd_WaitEventWait);
+
+int osd_WaitEventWaitEx(struct osd_waitevent *waitEvent, u32 TimeoutInMs)
+{
+ int ret = 0;
+
+ ret = wait_event_interruptible_timeout(waitEvent->event,
+ waitEvent->condition,
+ msecs_to_jiffies(TimeoutInMs));
+ waitEvent->condition = 0;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(osd_WaitEventWaitEx);
+
+static void osd_callback_work(struct work_struct *work)
+{
+ struct osd_callback_struct *cb = container_of(work,
+ struct osd_callback_struct,
+ work);
+ (cb->callback)(cb->data);
+ kfree(cb);
+}
+
+int osd_schedule_callback(struct workqueue_struct *wq,
+ void (*func)(void *),
+ void *data)
+{
+ struct osd_callback_struct *cb;
+
+ cb = kmalloc(sizeof(*cb), GFP_KERNEL);
+ if (!cb) {
+ printk(KERN_ERR "unable to allocate memory in osd_schedule_callback\n");
+ return -1;
+ }
+
+ cb->callback = func;
+ cb->data = data;
+ INIT_WORK(&cb->work, osd_callback_work);
+ return queue_work(wq, &cb->work);
+}
+
diff --git a/drivers/staging/hv/osd.h b/drivers/staging/hv/osd.h
new file mode 100644
index 000000000000..9504604c72bd
--- /dev/null
+++ b/drivers/staging/hv/osd.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _OSD_H_
+#define _OSD_H_
+
+
+/* Defines */
+#define ALIGN_UP(value, align) (((value) & (align-1)) ? \
+ (((value) + (align-1)) & ~(align-1)) : \
+ (value))
+#define ALIGN_DOWN(value, align) ((value) & ~(align-1))
+#define NUM_PAGES_SPANNED(addr, len) ((ALIGN_UP(addr+len, PAGE_SIZE) - \
+ ALIGN_DOWN(addr, PAGE_SIZE)) >> \
+ PAGE_SHIFT)
+
+#define LOWORD(dw) ((unsigned short)(dw))
+#define HIWORD(dw) ((unsigned short)(((unsigned int) (dw) >> 16) & 0xFFFF))
+
+struct hv_guid {
+ unsigned char data[16];
+};
+
+struct osd_waitevent {
+ int condition;
+ wait_queue_head_t event;
+};
+
+/* Osd routines */
+
+extern void *osd_VirtualAllocExec(unsigned int size);
+
+extern void *osd_PageAlloc(unsigned int count);
+extern void osd_PageFree(void *page, unsigned int count);
+
+extern struct osd_waitevent *osd_WaitEventCreate(void);
+extern void osd_WaitEventSet(struct osd_waitevent *waitEvent);
+extern int osd_WaitEventWait(struct osd_waitevent *waitEvent);
+
+/* If >0, waitEvent got signaled. If ==0, timeout. If < 0, error */
+extern int osd_WaitEventWaitEx(struct osd_waitevent *waitEvent,
+ u32 TimeoutInMs);
+
+int osd_schedule_callback(struct workqueue_struct *wq,
+ void (*func)(void *),
+ void *data);
+
+#endif /* _OSD_H_ */
diff --git a/drivers/staging/hv/rndis.h b/drivers/staging/hv/rndis.h
new file mode 100644
index 000000000000..7c73277c1f9a
--- /dev/null
+++ b/drivers/staging/hv/rndis.h
@@ -0,0 +1,652 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+#ifndef _RNDIS_H_
+#define _RNDIS_H_
+
+/* 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 RNdisMedium802_3 0x00000000
+#define RNdisMedium802_5 0x00000001
+#define RNdisMediumFddi 0x00000002
+#define RNdisMediumWan 0x00000003
+#define RNdisMediumLocalTalk 0x00000004
+#define RNdisMediumArcnetRaw 0x00000006
+#define RNdisMediumArcnet878_2 0x00000007
+#define RNdisMediumAtm 0x00000008
+#define RNdisMediumWirelessWan 0x00000009
+#define RNdisMediumIrda 0x0000000a
+#define RNdisMediumCoWan 0x0000000b
+/* Not a real medium, defined as an upper-bound */
+#define RNdisMediumMax 0x0000000d
+
+
+/* Remote NDIS medium connection states. */
+#define RNdisMediaStateConnected 0x00000000
+#define RNdisMediaStateDisconnected 0x00000001
+
+/* Remote NDIS version numbers */
+#define RNDIS_MAJOR_VERSION 0x00000001
+#define RNDIS_MINOR_VERSION 0x00000000
+
+
+/* NdisInitialize message */
+struct rndis_initialize_request {
+ u32 RequestId;
+ u32 MajorVersion;
+ u32 MinorVersion;
+ u32 MaxTransferSize;
+};
+
+/* Response to NdisInitialize */
+struct rndis_initialize_complete {
+ u32 RequestId;
+ u32 Status;
+ u32 MajorVersion;
+ u32 MinorVersion;
+ u32 DeviceFlags;
+ u32 Medium;
+ u32 MaxPacketsPerMessage;
+ u32 MaxTransferSize;
+ u32 PacketAlignmentFactor;
+ u32 AFListOffset;
+ u32 AFListSize;
+};
+
+/* 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 AddressFamily;
+ u32 MajorVersion;
+ u32 MinorVersion;
+};
+
+/* NdisHalt message */
+struct rndis_halt_request {
+ u32 RequestId;
+};
+
+/* NdisQueryRequest message */
+struct rndis_query_request {
+ u32 RequestId;
+ u32 Oid;
+ u32 InformationBufferLength;
+ u32 InformationBufferOffset;
+ u32 DeviceVcHandle;
+};
+
+/* Response to NdisQueryRequest */
+struct rndis_query_complete {
+ u32 RequestId;
+ u32 Status;
+ u32 InformationBufferLength;
+ u32 InformationBufferOffset;
+};
+
+/* NdisSetRequest message */
+struct rndis_set_request {
+ u32 RequestId;
+ u32 Oid;
+ u32 InformationBufferLength;
+ u32 InformationBufferOffset;
+ u32 DeviceVcHandle;
+};
+
+/* Response to NdisSetRequest */
+struct rndis_set_complete {
+ u32 RequestId;
+ u32 Status;
+};
+
+/* NdisReset message */
+struct rndis_reset_request {
+ u32 Reserved;
+};
+
+/* Response to NdisReset */
+struct rndis_reset_complete {
+ u32 Status;
+ u32 AddressingReset;
+};
+
+/* NdisMIndicateStatus message */
+struct rndis_indicate_status {
+ u32 Status;
+ u32 StatusBufferLength;
+ u32 StatusBufferOffset;
+};
+
+/* Diagnostic information passed as the status buffer in */
+/* struct rndis_indicate_status messages signifying error conditions. */
+struct rndis_diagnostic_info {
+ u32 DiagStatus;
+ u32 ErrorOffset;
+};
+
+/* NdisKeepAlive message */
+struct rndis_keepalive_request {
+ u32 RequestId;
+};
+
+/* Response to NdisKeepAlive */
+struct rndis_keepalive_complete {
+ u32 RequestId;
+ 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 DataOffset;
+ u32 DataLength;
+ u32 OOBDataOffset;
+ u32 OOBDataLength;
+ u32 NumOOBDataElements;
+ u32 PerPacketInfoOffset;
+ u32 PerPacketInfoLength;
+ u32 VcHandle;
+ u32 Reserved;
+};
+
+/* Optional Out of Band data associated with a Data message. */
+struct rndis_oobd {
+ u32 Size;
+ u32 Type;
+ u32 ClassInformationOffset;
+};
+
+/* Packet extension field contents associated with a Data message. */
+struct rndis_per_packet_info {
+ u32 Size;
+ u32 Type;
+ u32 PerPacketInformationOffset;
+};
+
+/* Format of Information buffer passed in a SetRequest for the OID */
+/* OID_GEN_RNDIS_CONFIG_PARAMETER. */
+struct rndis_config_parameter_info {
+ u32 ParameterNameOffset;
+ u32 ParameterNameLength;
+ u32 ParameterType;
+ u32 ParameterValueOffset;
+ u32 ParameterValueLength;
+};
+
+/* 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 RequestId;
+ u32 NdisVcHandle;
+};
+
+/* Response to CoNdisMiniportCreateVc */
+struct rcondis_mp_create_vc_complete {
+ u32 RequestId;
+ u32 DeviceVcHandle;
+ u32 Status;
+};
+
+/* CoNdisMiniportDeleteVc message */
+struct rcondis_mp_delete_vc {
+ u32 RequestId;
+ u32 DeviceVcHandle;
+};
+
+/* Response to CoNdisMiniportDeleteVc */
+struct rcondis_mp_delete_vc_complete {
+ u32 RequestId;
+ u32 Status;
+};
+
+/* CoNdisMiniportQueryRequest message */
+struct rcondis_mp_query_request {
+ u32 RequestId;
+ u32 RequestType;
+ u32 Oid;
+ u32 DeviceVcHandle;
+ u32 InformationBufferLength;
+ u32 InformationBufferOffset;
+};
+
+/* CoNdisMiniportSetRequest message */
+struct rcondis_mp_set_request {
+ u32 RequestId;
+ u32 RequestType;
+ u32 Oid;
+ u32 DeviceVcHandle;
+ u32 InformationBufferLength;
+ u32 InformationBufferOffset;
+};
+
+/* CoNdisIndicateStatus message */
+struct rcondis_indicate_status {
+ u32 NdisVcHandle;
+ u32 Status;
+ u32 StatusBufferLength;
+ u32 StatusBufferOffset;
+};
+
+/* CONDIS Call/VC parameters */
+struct rcondis_specific_parameters {
+ u32 ParameterType;
+ u32 ParameterLength;
+ u32 ParameterOffset;
+};
+
+struct rcondis_media_parameters {
+ u32 Flags;
+ u32 Reserved1;
+ u32 Reserved2;
+ struct rcondis_specific_parameters MediaSpecific;
+};
+
+struct rndis_flowspec {
+ u32 TokenRate;
+ u32 TokenBucketSize;
+ u32 PeakBandwidth;
+ u32 Latency;
+ u32 DelayVariation;
+ u32 ServiceType;
+ u32 MaxSduSize;
+ u32 MinimumPolicedSize;
+};
+
+struct rcondis_call_manager_parameters {
+ struct rndis_flowspec Transmit;
+ struct rndis_flowspec Receive;
+ struct rcondis_specific_parameters CallMgrSpecific;
+};
+
+/* CoNdisMiniportActivateVc message */
+struct rcondis_mp_activate_vc_request {
+ u32 RequestId;
+ u32 Flags;
+ u32 DeviceVcHandle;
+ u32 MediaParamsOffset;
+ u32 MediaParamsLength;
+ u32 CallMgrParamsOffset;
+ u32 CallMgrParamsLength;
+};
+
+/* Response to CoNdisMiniportActivateVc */
+struct rcondis_mp_activate_vc_complete {
+ u32 RequestId;
+ u32 Status;
+};
+
+/* CoNdisMiniportDeactivateVc message */
+struct rcondis_mp_deactivate_vc_request {
+ u32 RequestId;
+ u32 Flags;
+ u32 DeviceVcHandle;
+};
+
+/* Response to CoNdisMiniportDeactivateVc */
+struct rcondis_mp_deactivate_vc_complete {
+ u32 RequestId;
+ u32 Status;
+};
+
+
+/* union with all of the RNDIS messages */
+union rndis_message_container {
+ struct rndis_packet Packet;
+ struct rndis_initialize_request InitializeRequest;
+ struct rndis_halt_request HaltRequest;
+ struct rndis_query_request QueryRequest;
+ struct rndis_set_request SetRequest;
+ struct rndis_reset_request ResetRequest;
+ struct rndis_keepalive_request KeepaliveRequest;
+ struct rndis_indicate_status IndicateStatus;
+ struct rndis_initialize_complete InitializeComplete;
+ struct rndis_query_complete QueryComplete;
+ struct rndis_set_complete SetComplete;
+ struct rndis_reset_complete ResetComplete;
+ struct rndis_keepalive_complete KeepaliveComplete;
+ struct rcondis_mp_create_vc CoMiniportCreateVc;
+ struct rcondis_mp_delete_vc CoMiniportDeleteVc;
+ struct rcondis_indicate_status CoIndicateStatus;
+ struct rcondis_mp_activate_vc_request CoMiniportActivateVc;
+ struct rcondis_mp_deactivate_vc_request CoMiniportDeactivateVc;
+ struct rcondis_mp_create_vc_complete CoMiniportCreateVcComplete;
+ struct rcondis_mp_delete_vc_complete CoMiniportDeleteVcComplete;
+ struct rcondis_mp_activate_vc_complete CoMiniportActivateVcComplete;
+ struct rcondis_mp_deactivate_vc_complete CoMiniportDeactivateVcComplete;
+};
+
+/* Remote NDIS message format */
+struct rndis_message {
+ u32 NdisMessageType;
+
+ /* Total length of this message, from the beginning */
+ /* of the sruct rndis_message, in bytes. */
+ u32 MessageLength;
+
+ /* Actual message */
+ union rndis_message_container Message;
+};
+
+/* 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(Message) \
+ (sizeof(Message) + (sizeof(struct rndis_message) - \
+ sizeof(union rndis_message_container)))
+
+/* get pointer to info buffer with message pointer */
+#define MESSAGE_TO_INFO_BUFFER(Message) \
+ (((unsigned char *)(Message)) + Message->InformationBufferOffset)
+
+/* get pointer to status buffer with message pointer */
+#define MESSAGE_TO_STATUS_BUFFER(Message) \
+ (((unsigned char *)(Message)) + Message->StatusBufferOffset)
+
+/* get pointer to OOBD buffer with message pointer */
+#define MESSAGE_TO_OOBD_BUFFER(Message) \
+ (((unsigned char *)(Message)) + Message->OOBDataOffset)
+
+/* get pointer to data buffer with message pointer */
+#define MESSAGE_TO_DATA_BUFFER(Message) \
+ (((unsigned char *)(Message)) + Message->PerPacketInfoOffset)
+
+/* get pointer to contained message from NDIS_MESSAGE pointer */
+#define RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(RndisMessage) \
+ ((void *) &RndisMessage->Message)
+
+/* get pointer to contained message from NDIS_MESSAGE pointer */
+#define RNDIS_MESSAGE_RAW_PTR_TO_MESSAGE_PTR(RndisMessage) \
+ ((void *) RndisMessage)
+
+#endif /* _RNDIS_H_ */
diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c
new file mode 100644
index 000000000000..d49dc21d4cb4
--- /dev/null
+++ b/drivers/staging/hv/storvsc_drv.c
@@ -0,0 +1,1208 @@
+/*
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_devinfo.h>
+#include <scsi/scsi_dbg.h>
+#include "osd.h"
+#include "logging.h"
+#include "vmbus.h"
+#include "StorVscApi.h"
+
+
+struct host_device_context {
+ /* must be 1st field
+ * FIXME this is a bug */
+ struct work_struct host_rescan_work;
+
+ /* point back to our device context */
+ struct device_context *device_ctx;
+ struct kmem_cache *request_pool;
+ unsigned int port;
+ unsigned char path;
+ unsigned char target;
+};
+
+struct storvsc_cmd_request {
+ struct list_head entry;
+ struct scsi_cmnd *cmd;
+
+ unsigned int bounce_sgl_count;
+ struct scatterlist *bounce_sgl;
+
+ struct hv_storvsc_request request;
+ /* !!!DO NOT ADD ANYTHING BELOW HERE!!! */
+ /* The extension buffer falls right here and is pointed to by
+ * request.Extension;
+ * Which sounds like a very bad design... */
+};
+
+struct storvsc_driver_context {
+ /* !! These must be the first 2 fields !! */
+ /* FIXME this is a bug... */
+ struct driver_context drv_ctx;
+ struct storvsc_driver_object drv_obj;
+};
+
+/* Static decl */
+static int storvsc_probe(struct device *dev);
+static int storvsc_queuecommand(struct scsi_cmnd *scmnd,
+ void (*done)(struct scsi_cmnd *));
+static int storvsc_device_alloc(struct scsi_device *);
+static int storvsc_device_configure(struct scsi_device *);
+static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd);
+static void storvsc_host_rescan_callback(struct work_struct *work);
+static void storvsc_host_rescan(struct hv_device *device_obj);
+static int storvsc_remove(struct device *dev);
+
+static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl,
+ unsigned int sg_count,
+ unsigned int len);
+static void destroy_bounce_buffer(struct scatterlist *sgl,
+ unsigned int sg_count);
+static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count);
+static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl,
+ struct scatterlist *bounce_sgl,
+ unsigned int orig_sgl_count);
+static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl,
+ struct scatterlist *bounce_sgl,
+ unsigned int orig_sgl_count);
+
+static int storvsc_report_luns(struct scsi_device *sdev, unsigned int luns[],
+ unsigned int *lun_count);
+static int storvsc_get_chs(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int *info);
+
+
+static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE;
+
+/* The one and only one */
+static struct storvsc_driver_context g_storvsc_drv;
+
+/* Scsi driver */
+static struct scsi_host_template scsi_driver = {
+ .module = THIS_MODULE,
+ .name = "storvsc_host_t",
+ .bios_param = storvsc_get_chs,
+ .queuecommand = storvsc_queuecommand,
+ .eh_host_reset_handler = storvsc_host_reset_handler,
+ .slave_alloc = storvsc_device_alloc,
+ .slave_configure = storvsc_device_configure,
+ .cmd_per_lun = 1,
+ /* 64 max_queue * 1 target */
+ .can_queue = STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS,
+ .this_id = -1,
+ /* no use setting to 0 since ll_blk_rw reset it to 1 */
+ /* currently 32 */
+ .sg_tablesize = MAX_MULTIPAGE_BUFFER_COUNT,
+ /*
+ * ENABLE_CLUSTERING allows mutiple physically contig bio_vecs to merge
+ * into 1 sg element. If set, we must limit the max_segment_size to
+ * PAGE_SIZE, otherwise we may get 1 sg element that represents
+ * multiple
+ */
+ /* physically contig pfns (ie sg[x].length > PAGE_SIZE). */
+ .use_clustering = ENABLE_CLUSTERING,
+ /* Make sure we dont get a sg segment crosses a page boundary */
+ .dma_boundary = PAGE_SIZE-1,
+};
+
+
+/**
+ * storvsc_drv_init - StorVsc driver initialization.
+ */
+static int storvsc_drv_init(int (*drv_init)(struct hv_driver *drv))
+{
+ int ret;
+ struct storvsc_driver_object *storvsc_drv_obj = &g_storvsc_drv.drv_obj;
+ struct driver_context *drv_ctx = &g_storvsc_drv.drv_ctx;
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ vmbus_get_interface(&storvsc_drv_obj->Base.VmbusChannelInterface);
+
+ storvsc_drv_obj->RingBufferSize = storvsc_ringbuffer_size;
+ storvsc_drv_obj->OnHostRescan = storvsc_host_rescan;
+
+ /* Callback to client driver to complete the initialization */
+ drv_init(&storvsc_drv_obj->Base);
+
+ DPRINT_INFO(STORVSC_DRV,
+ "request extension size %u, max outstanding reqs %u",
+ storvsc_drv_obj->RequestExtSize,
+ storvsc_drv_obj->MaxOutstandingRequestsPerChannel);
+
+ if (storvsc_drv_obj->MaxOutstandingRequestsPerChannel <
+ STORVSC_MAX_IO_REQUESTS) {
+ DPRINT_ERR(STORVSC_DRV,
+ "The number of outstanding io requests (%d) "
+ "is larger than that supported (%d) internally.",
+ STORVSC_MAX_IO_REQUESTS,
+ storvsc_drv_obj->MaxOutstandingRequestsPerChannel);
+ return -1;
+ }
+
+ drv_ctx->driver.name = storvsc_drv_obj->Base.name;
+ memcpy(&drv_ctx->class_id, &storvsc_drv_obj->Base.deviceType,
+ sizeof(struct hv_guid));
+
+ drv_ctx->probe = storvsc_probe;
+ drv_ctx->remove = storvsc_remove;
+
+ /* The driver belongs to vmbus */
+ ret = vmbus_child_driver_register(drv_ctx);
+
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return ret;
+}
+
+static int storvsc_drv_exit_cb(struct device *dev, void *data)
+{
+ struct device **curr = (struct device **)data;
+ *curr = dev;
+ return 1; /* stop iterating */
+}
+
+static void storvsc_drv_exit(void)
+{
+ struct storvsc_driver_object *storvsc_drv_obj = &g_storvsc_drv.drv_obj;
+ struct driver_context *drv_ctx = &g_storvsc_drv.drv_ctx;
+ struct device *current_dev = NULL;
+ int ret;
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ while (1) {
+ current_dev = NULL;
+
+ /* Get the device */
+ ret = driver_for_each_device(&drv_ctx->driver, NULL,
+ (void *) &current_dev,
+ storvsc_drv_exit_cb);
+
+ if (ret)
+ DPRINT_WARN(STORVSC_DRV,
+ "driver_for_each_device returned %d", ret);
+
+ if (current_dev == NULL)
+ break;
+
+ /* Initiate removal from the top-down */
+ device_unregister(current_dev);
+ }
+
+ if (storvsc_drv_obj->Base.OnCleanup)
+ storvsc_drv_obj->Base.OnCleanup(&storvsc_drv_obj->Base);
+
+ vmbus_child_driver_unregister(drv_ctx);
+
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return;
+}
+
+/**
+ * storvsc_probe - Add a new device for this driver
+ */
+static int storvsc_probe(struct device *device)
+{
+ int ret;
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device->driver);
+ struct storvsc_driver_context *storvsc_drv_ctx =
+ (struct storvsc_driver_context *)driver_ctx;
+ struct storvsc_driver_object *storvsc_drv_obj =
+ &storvsc_drv_ctx->drv_obj;
+ struct device_context *device_ctx = device_to_device_context(device);
+ struct hv_device *device_obj = &device_ctx->device_obj;
+ struct Scsi_Host *host;
+ struct host_device_context *host_device_ctx;
+ struct storvsc_device_info device_info;
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ if (!storvsc_drv_obj->Base.OnDeviceAdd)
+ return -1;
+
+ host = scsi_host_alloc(&scsi_driver,
+ sizeof(struct host_device_context));
+ if (!host) {
+ DPRINT_ERR(STORVSC_DRV, "unable to allocate scsi host object");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(device, host);
+
+ host_device_ctx = (struct host_device_context *)host->hostdata;
+ memset(host_device_ctx, 0, sizeof(struct host_device_context));
+
+ host_device_ctx->port = host->host_no;
+ host_device_ctx->device_ctx = device_ctx;
+
+ INIT_WORK(&host_device_ctx->host_rescan_work,
+ storvsc_host_rescan_callback);
+
+ host_device_ctx->request_pool =
+ kmem_cache_create(dev_name(&device_ctx->device),
+ sizeof(struct storvsc_cmd_request) +
+ storvsc_drv_obj->RequestExtSize, 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+
+ if (!host_device_ctx->request_pool) {
+ scsi_host_put(host);
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return -ENOMEM;
+ }
+
+ device_info.PortNumber = host->host_no;
+ /* Call to the vsc driver to add the device */
+ ret = storvsc_drv_obj->Base.OnDeviceAdd(device_obj,
+ (void *)&device_info);
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC_DRV, "unable to add scsi vsc device");
+ kmem_cache_destroy(host_device_ctx->request_pool);
+ scsi_host_put(host);
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return -1;
+ }
+
+ /* host_device_ctx->port = device_info.PortNumber; */
+ host_device_ctx->path = device_info.PathId;
+ host_device_ctx->target = device_info.TargetId;
+
+ /* max # of devices per target */
+ host->max_lun = STORVSC_MAX_LUNS_PER_TARGET;
+ /* max # of targets per channel */
+ host->max_id = STORVSC_MAX_TARGETS;
+ /* max # of channels */
+ host->max_channel = STORVSC_MAX_CHANNELS - 1;
+
+ /* Register the HBA and start the scsi bus scan */
+ ret = scsi_add_host(host, device);
+ if (ret != 0) {
+ DPRINT_ERR(STORVSC_DRV, "unable to add scsi host device");
+
+ storvsc_drv_obj->Base.OnDeviceRemove(device_obj);
+
+ kmem_cache_destroy(host_device_ctx->request_pool);
+ scsi_host_put(host);
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return -1;
+ }
+
+ scsi_scan_host(host);
+
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return ret;
+}
+
+/**
+ * storvsc_remove - Callback when our device is removed
+ */
+static int storvsc_remove(struct device *device)
+{
+ int ret;
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device->driver);
+ struct storvsc_driver_context *storvsc_drv_ctx =
+ (struct storvsc_driver_context *)driver_ctx;
+ struct storvsc_driver_object *storvsc_drv_obj =
+ &storvsc_drv_ctx->drv_obj;
+ struct device_context *device_ctx = device_to_device_context(device);
+ struct hv_device *device_obj = &device_ctx->device_obj;
+ struct Scsi_Host *host = dev_get_drvdata(device);
+ struct host_device_context *host_device_ctx =
+ (struct host_device_context *)host->hostdata;
+
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ if (!storvsc_drv_obj->Base.OnDeviceRemove) {
+ DPRINT_EXIT(STORVSC_DRV);
+ return -1;
+ }
+
+ /*
+ * Call to the vsc driver to let it know that the device is being
+ * removed
+ */
+ ret = storvsc_drv_obj->Base.OnDeviceRemove(device_obj);
+ if (ret != 0) {
+ /* TODO: */
+ DPRINT_ERR(STORVSC, "unable to remove vsc device (ret %d)",
+ ret);
+ }
+
+ if (host_device_ctx->request_pool) {
+ kmem_cache_destroy(host_device_ctx->request_pool);
+ host_device_ctx->request_pool = NULL;
+ }
+
+ DPRINT_INFO(STORVSC, "removing host adapter (%p)...", host);
+ scsi_remove_host(host);
+
+ DPRINT_INFO(STORVSC, "releasing host adapter (%p)...", host);
+ scsi_host_put(host);
+
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return ret;
+}
+
+/**
+ * storvsc_commmand_completion - Command completion processing
+ */
+static void storvsc_commmand_completion(struct hv_storvsc_request *request)
+{
+ struct storvsc_cmd_request *cmd_request =
+ (struct storvsc_cmd_request *)request->Context;
+ struct scsi_cmnd *scmnd = cmd_request->cmd;
+ struct host_device_context *host_device_ctx =
+ (struct host_device_context *)scmnd->device->host->hostdata;
+ void (*scsi_done_fn)(struct scsi_cmnd *);
+ struct scsi_sense_hdr sense_hdr;
+
+ ASSERT(request == &cmd_request->request);
+ ASSERT((unsigned long)scmnd->host_scribble ==
+ (unsigned long)cmd_request);
+ ASSERT(scmnd);
+ ASSERT(scmnd->scsi_done);
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ if (cmd_request->bounce_sgl_count) {
+ /* using bounce buffer */
+ /* printk("copy_from_bounce_buffer\n"); */
+
+ /* FIXME: We can optimize on writes by just skipping this */
+ copy_from_bounce_buffer(scsi_sglist(scmnd),
+ cmd_request->bounce_sgl,
+ scsi_sg_count(scmnd));
+ destroy_bounce_buffer(cmd_request->bounce_sgl,
+ cmd_request->bounce_sgl_count);
+ }
+
+ scmnd->result = request->Status;
+
+ if (scmnd->result) {
+ if (scsi_normalize_sense(scmnd->sense_buffer,
+ request->SenseBufferSize, &sense_hdr))
+ scsi_print_sense_hdr("storvsc", &sense_hdr);
+ }
+
+ ASSERT(request->BytesXfer <= request->DataBuffer.Length);
+ scsi_set_resid(scmnd, request->DataBuffer.Length - request->BytesXfer);
+
+ scsi_done_fn = scmnd->scsi_done;
+
+ scmnd->host_scribble = NULL;
+ scmnd->scsi_done = NULL;
+
+ /* !!DO NOT MODIFY the scmnd after this call */
+ scsi_done_fn(scmnd);
+
+ kmem_cache_free(host_device_ctx->request_pool, cmd_request);
+
+ DPRINT_EXIT(STORVSC_DRV);
+}
+
+static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count)
+{
+ int i;
+
+ /* No need to check */
+ if (sg_count < 2)
+ return -1;
+
+ /* We have at least 2 sg entries */
+ for (i = 0; i < sg_count; i++) {
+ if (i == 0) {
+ /* make sure 1st one does not have hole */
+ if (sgl[i].offset + sgl[i].length != PAGE_SIZE)
+ return i;
+ } else if (i == sg_count - 1) {
+ /* make sure last one does not have hole */
+ if (sgl[i].offset != 0)
+ return i;
+ } else {
+ /* make sure no hole in the middle */
+ if (sgl[i].length != PAGE_SIZE || sgl[i].offset != 0)
+ return i;
+ }
+ }
+ return -1;
+}
+
+static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl,
+ unsigned int sg_count,
+ unsigned int len)
+{
+ int i;
+ int num_pages;
+ struct scatterlist *bounce_sgl;
+ struct page *page_buf;
+
+ num_pages = ALIGN_UP(len, PAGE_SIZE) >> PAGE_SHIFT;
+
+ bounce_sgl = kcalloc(num_pages, sizeof(struct scatterlist), GFP_ATOMIC);
+ if (!bounce_sgl)
+ return NULL;
+
+ for (i = 0; i < num_pages; i++) {
+ page_buf = alloc_page(GFP_ATOMIC);
+ if (!page_buf)
+ goto cleanup;
+ sg_set_page(&bounce_sgl[i], page_buf, 0, 0);
+ }
+
+ return bounce_sgl;
+
+cleanup:
+ destroy_bounce_buffer(bounce_sgl, num_pages);
+ return NULL;
+}
+
+static void destroy_bounce_buffer(struct scatterlist *sgl,
+ unsigned int sg_count)
+{
+ int i;
+ struct page *page_buf;
+
+ for (i = 0; i < sg_count; i++) {
+ page_buf = sg_page((&sgl[i]));
+ if (page_buf != NULL)
+ __free_page(page_buf);
+ }
+
+ kfree(sgl);
+}
+
+/* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */
+static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl,
+ struct scatterlist *bounce_sgl,
+ unsigned int orig_sgl_count)
+{
+ int i;
+ int j = 0;
+ unsigned long src, dest;
+ unsigned int srclen, destlen, copylen;
+ unsigned int total_copied = 0;
+ unsigned long bounce_addr = 0;
+ unsigned long src_addr = 0;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ for (i = 0; i < orig_sgl_count; i++) {
+ src_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])),
+ KM_IRQ0) + orig_sgl[i].offset;
+ src = src_addr;
+ srclen = orig_sgl[i].length;
+
+ ASSERT(orig_sgl[i].offset + orig_sgl[i].length <= PAGE_SIZE);
+
+ if (j == 0)
+ bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0);
+
+ while (srclen) {
+ /* assume bounce offset always == 0 */
+ dest = bounce_addr + bounce_sgl[j].length;
+ destlen = PAGE_SIZE - bounce_sgl[j].length;
+
+ copylen = min(srclen, destlen);
+ memcpy((void *)dest, (void *)src, copylen);
+
+ total_copied += copylen;
+ bounce_sgl[j].length += copylen;
+ srclen -= copylen;
+ src += copylen;
+
+ if (bounce_sgl[j].length == PAGE_SIZE) {
+ /* full..move to next entry */
+ kunmap_atomic((void *)bounce_addr, KM_IRQ0);
+ j++;
+
+ /* if we need to use another bounce buffer */
+ if (srclen || i != orig_sgl_count - 1)
+ bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0);
+ } else if (srclen == 0 && i == orig_sgl_count - 1) {
+ /* unmap the last bounce that is < PAGE_SIZE */
+ kunmap_atomic((void *)bounce_addr, KM_IRQ0);
+ }
+ }
+
+ kunmap_atomic((void *)(src_addr - orig_sgl[i].offset), KM_IRQ0);
+ }
+
+ local_irq_restore(flags);
+
+ return total_copied;
+}
+
+/* Assume the original sgl has enough room */
+static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl,
+ struct scatterlist *bounce_sgl,
+ unsigned int orig_sgl_count)
+{
+ int i;
+ int j = 0;
+ unsigned long src, dest;
+ unsigned int srclen, destlen, copylen;
+ unsigned int total_copied = 0;
+ unsigned long bounce_addr = 0;
+ unsigned long dest_addr = 0;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ for (i = 0; i < orig_sgl_count; i++) {
+ dest_addr = (unsigned long)kmap_atomic(sg_page((&orig_sgl[i])),
+ KM_IRQ0) + orig_sgl[i].offset;
+ dest = dest_addr;
+ destlen = orig_sgl[i].length;
+ ASSERT(orig_sgl[i].offset + orig_sgl[i].length <= PAGE_SIZE);
+
+ if (j == 0)
+ bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0);
+
+ while (destlen) {
+ src = bounce_addr + bounce_sgl[j].offset;
+ srclen = bounce_sgl[j].length - bounce_sgl[j].offset;
+
+ copylen = min(srclen, destlen);
+ memcpy((void *)dest, (void *)src, copylen);
+
+ total_copied += copylen;
+ bounce_sgl[j].offset += copylen;
+ destlen -= copylen;
+ dest += copylen;
+
+ if (bounce_sgl[j].offset == bounce_sgl[j].length) {
+ /* full */
+ kunmap_atomic((void *)bounce_addr, KM_IRQ0);
+ j++;
+
+ /* if we need to use another bounce buffer */
+ if (destlen || i != orig_sgl_count - 1)
+ bounce_addr = (unsigned long)kmap_atomic(sg_page((&bounce_sgl[j])), KM_IRQ0);
+ } else if (destlen == 0 && i == orig_sgl_count - 1) {
+ /* unmap the last bounce that is < PAGE_SIZE */
+ kunmap_atomic((void *)bounce_addr, KM_IRQ0);
+ }
+ }
+
+ kunmap_atomic((void *)(dest_addr - orig_sgl[i].offset),
+ KM_IRQ0);
+ }
+
+ local_irq_restore(flags);
+
+ return total_copied;
+}
+
+/**
+ * storvsc_queuecommand - Initiate command processing
+ */
+static int storvsc_queuecommand(struct scsi_cmnd *scmnd,
+ void (*done)(struct scsi_cmnd *))
+{
+ int ret;
+ struct host_device_context *host_device_ctx =
+ (struct host_device_context *)scmnd->device->host->hostdata;
+ struct device_context *device_ctx = host_device_ctx->device_ctx;
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device_ctx->device.driver);
+ struct storvsc_driver_context *storvsc_drv_ctx =
+ (struct storvsc_driver_context *)driver_ctx;
+ struct storvsc_driver_object *storvsc_drv_obj =
+ &storvsc_drv_ctx->drv_obj;
+ struct hv_storvsc_request *request;
+ struct storvsc_cmd_request *cmd_request;
+ unsigned int request_size = 0;
+ int i;
+ struct scatterlist *sgl;
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ DPRINT_DBG(STORVSC_DRV, "scmnd %p dir %d, use_sg %d buf %p len %d "
+ "queue depth %d tagged %d", scmnd, scmnd->sc_data_direction,
+ scsi_sg_count(scmnd), scsi_sglist(scmnd),
+ scsi_bufflen(scmnd), scmnd->device->queue_depth,
+ scmnd->device->tagged_supported);
+
+ /* If retrying, no need to prep the cmd */
+ if (scmnd->host_scribble) {
+ ASSERT(scmnd->scsi_done != NULL);
+
+ cmd_request =
+ (struct storvsc_cmd_request *)scmnd->host_scribble;
+ DPRINT_INFO(STORVSC_DRV, "retrying scmnd %p cmd_request %p",
+ scmnd, cmd_request);
+
+ goto retry_request;
+ }
+
+ ASSERT(scmnd->scsi_done == NULL);
+ ASSERT(scmnd->host_scribble == NULL);
+
+ scmnd->scsi_done = done;
+
+ request_size = sizeof(struct storvsc_cmd_request);
+
+ cmd_request = kmem_cache_alloc(host_device_ctx->request_pool,
+ GFP_ATOMIC);
+ if (!cmd_request) {
+ DPRINT_ERR(STORVSC_DRV, "scmnd (%p) - unable to allocate "
+ "storvsc_cmd_request...marking queue busy", scmnd);
+ scmnd->scsi_done = NULL;
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+
+ /* Setup the cmd request */
+ cmd_request->bounce_sgl_count = 0;
+ cmd_request->bounce_sgl = NULL;
+ cmd_request->cmd = scmnd;
+
+ scmnd->host_scribble = (unsigned char *)cmd_request;
+
+ request = &cmd_request->request;
+
+ request->Extension =
+ (void *)((unsigned long)cmd_request + request_size);
+ DPRINT_DBG(STORVSC_DRV, "req %p size %d ext %d", request, request_size,
+ storvsc_drv_obj->RequestExtSize);
+
+ /* Build the SRB */
+ switch (scmnd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ request->Type = WRITE_TYPE;
+ break;
+ case DMA_FROM_DEVICE:
+ request->Type = READ_TYPE;
+ break;
+ default:
+ request->Type = UNKNOWN_TYPE;
+ break;
+ }
+
+ request->OnIOCompletion = storvsc_commmand_completion;
+ request->Context = cmd_request;/* scmnd; */
+
+ /* request->PortId = scmnd->device->channel; */
+ request->Host = host_device_ctx->port;
+ request->Bus = scmnd->device->channel;
+ request->TargetId = scmnd->device->id;
+ request->LunId = scmnd->device->lun;
+
+ ASSERT(scmnd->cmd_len <= 16);
+ request->CdbLen = scmnd->cmd_len;
+ request->Cdb = scmnd->cmnd;
+
+ request->SenseBuffer = scmnd->sense_buffer;
+ request->SenseBufferSize = SCSI_SENSE_BUFFERSIZE;
+
+
+ request->DataBuffer.Length = scsi_bufflen(scmnd);
+ if (scsi_sg_count(scmnd)) {
+ sgl = (struct scatterlist *)scsi_sglist(scmnd);
+
+ /* check if we need to bounce the sgl */
+ if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) {
+ DPRINT_INFO(STORVSC_DRV,
+ "need to bounce buffer for this scmnd %p",
+ scmnd);
+ cmd_request->bounce_sgl =
+ create_bounce_buffer(sgl, scsi_sg_count(scmnd),
+ scsi_bufflen(scmnd));
+ if (!cmd_request->bounce_sgl) {
+ DPRINT_ERR(STORVSC_DRV,
+ "unable to create bounce buffer for "
+ "this scmnd %p", scmnd);
+
+ scmnd->scsi_done = NULL;
+ scmnd->host_scribble = NULL;
+ kmem_cache_free(host_device_ctx->request_pool,
+ cmd_request);
+
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ cmd_request->bounce_sgl_count =
+ ALIGN_UP(scsi_bufflen(scmnd), PAGE_SIZE) >>
+ PAGE_SHIFT;
+
+ /*
+ * FIXME: We can optimize on reads by just skipping
+ * this
+ */
+ copy_to_bounce_buffer(sgl, cmd_request->bounce_sgl,
+ scsi_sg_count(scmnd));
+
+ sgl = cmd_request->bounce_sgl;
+ }
+
+ request->DataBuffer.Offset = sgl[0].offset;
+
+ for (i = 0; i < scsi_sg_count(scmnd); i++) {
+ DPRINT_DBG(STORVSC_DRV, "sgl[%d] len %d offset %d \n",
+ i, sgl[i].length, sgl[i].offset);
+ request->DataBuffer.PfnArray[i] =
+ page_to_pfn(sg_page((&sgl[i])));
+ }
+ } else if (scsi_sglist(scmnd)) {
+ ASSERT(scsi_bufflen(scmnd) <= PAGE_SIZE);
+ request->DataBuffer.Offset =
+ virt_to_phys(scsi_sglist(scmnd)) & (PAGE_SIZE-1);
+ request->DataBuffer.PfnArray[0] =
+ virt_to_phys(scsi_sglist(scmnd)) >> PAGE_SHIFT;
+ } else {
+ ASSERT(scsi_bufflen(scmnd) == 0);
+ }
+
+retry_request:
+ /* Invokes the vsc to start an IO */
+ ret = storvsc_drv_obj->OnIORequest(&device_ctx->device_obj,
+ &cmd_request->request);
+ if (ret == -1) {
+ /* no more space */
+ DPRINT_ERR(STORVSC_DRV,
+ "scmnd (%p) - queue FULL...marking queue busy",
+ scmnd);
+
+ if (cmd_request->bounce_sgl_count) {
+ /*
+ * FIXME: We can optimize on writes by just skipping
+ * this
+ */
+ copy_from_bounce_buffer(scsi_sglist(scmnd),
+ cmd_request->bounce_sgl,
+ scsi_sg_count(scmnd));
+ destroy_bounce_buffer(cmd_request->bounce_sgl,
+ cmd_request->bounce_sgl_count);
+ }
+
+ kmem_cache_free(host_device_ctx->request_pool, cmd_request);
+
+ scmnd->scsi_done = NULL;
+ scmnd->host_scribble = NULL;
+
+ ret = SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return ret;
+}
+
+static int storvsc_merge_bvec(struct request_queue *q,
+ struct bvec_merge_data *bmd, struct bio_vec *bvec)
+{
+ /* checking done by caller. */
+ return bvec->bv_len;
+}
+
+/**
+ * storvsc_device_configure - Configure the specified scsi device
+ */
+static int storvsc_device_alloc(struct scsi_device *sdevice)
+{
+ DPRINT_DBG(STORVSC_DRV, "sdev (%p) - setting device flag to %d",
+ sdevice, BLIST_SPARSELUN);
+ /*
+ * This enables luns to be located sparsely. Otherwise, we may not
+ * discovered them.
+ */
+ sdevice->sdev_bflags |= BLIST_SPARSELUN | BLIST_LARGELUN;
+ return 0;
+}
+
+static int storvsc_device_configure(struct scsi_device *sdevice)
+{
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) - curr queue depth %d", sdevice,
+ sdevice->queue_depth);
+
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) - setting queue depth to %d",
+ sdevice, STORVSC_MAX_IO_REQUESTS);
+ scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG,
+ STORVSC_MAX_IO_REQUESTS);
+
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) - setting max segment size to %ld",
+ sdevice, PAGE_SIZE);
+ blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE);
+
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) - adding merge bio vec routine",
+ sdevice);
+ blk_queue_merge_bvec(sdevice->request_queue, storvsc_merge_bvec);
+
+ blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY);
+ /* sdevice->timeout = (2000 * HZ);//(75 * HZ); */
+
+ return 0;
+}
+
+/**
+ * storvsc_host_reset_handler - Reset the scsi HBA
+ */
+static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd)
+{
+ int ret;
+ struct host_device_context *host_device_ctx =
+ (struct host_device_context *)scmnd->device->host->hostdata;
+ struct device_context *device_ctx = host_device_ctx->device_ctx;
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(device_ctx->device.driver);
+ struct storvsc_driver_context *storvsc_drv_ctx =
+ (struct storvsc_driver_context *)driver_ctx;
+
+ struct storvsc_driver_object *storvsc_drv_obj =
+ &storvsc_drv_ctx->drv_obj;
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host resetting...",
+ scmnd->device, &device_ctx->device_obj);
+
+ /* Invokes the vsc to reset the host/bus */
+ ASSERT(storvsc_drv_obj->OnHostReset);
+ ret = storvsc_drv_obj->OnHostReset(&device_ctx->device_obj);
+ if (ret != 0) {
+ DPRINT_EXIT(STORVSC_DRV);
+ return ret;
+ }
+
+ DPRINT_INFO(STORVSC_DRV, "sdev (%p) dev obj (%p) - host reseted",
+ scmnd->device, &device_ctx->device_obj);
+
+ DPRINT_EXIT(STORVSC_DRV);
+
+ return ret;
+}
+
+/**
+ * storvsc_host_rescan - Rescan the scsi HBA
+ */
+static void storvsc_host_rescan_callback(struct work_struct *work)
+{
+ struct hv_device *device_obj =
+ &((struct host_device_context *)work)->device_ctx->device_obj;
+ struct device_context *device_ctx = to_device_context(device_obj);
+ struct Scsi_Host *host = dev_get_drvdata(&device_ctx->device);
+ struct scsi_device *sdev;
+ struct host_device_context *host_device_ctx;
+ struct scsi_device **sdevs_remove_list;
+ unsigned int sdevs_count = 0;
+ unsigned int found;
+ unsigned int i;
+ unsigned int lun_count = 0;
+ unsigned int *lun_list;
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ host_device_ctx = (struct host_device_context *)host->hostdata;
+ lun_list = kcalloc(STORVSC_MAX_LUNS_PER_TARGET, sizeof(unsigned int),
+ GFP_ATOMIC);
+ if (!lun_list) {
+ DPRINT_ERR(STORVSC_DRV, "unable to allocate lun list");
+ return;
+ }
+
+ sdevs_remove_list = kcalloc(STORVSC_MAX_LUNS_PER_TARGET,
+ sizeof(void *), GFP_ATOMIC);
+ if (!sdevs_remove_list) {
+ kfree(lun_list);
+ DPRINT_ERR(STORVSC_DRV, "unable to allocate lun remove list");
+ return;
+ }
+
+ DPRINT_INFO(STORVSC_DRV, "rescanning host for new scsi devices...");
+
+ /* Rescan for new device */
+ scsi_scan_target(&host->shost_gendev, host_device_ctx->path,
+ host_device_ctx->target, SCAN_WILD_CARD, 1);
+
+ DPRINT_INFO(STORVSC_DRV, "rescanning host for removed scsi device...");
+
+ /* Use the 1st device to send the report luns cmd */
+ shost_for_each_device(sdev, host) {
+ lun_count = STORVSC_MAX_LUNS_PER_TARGET;
+ storvsc_report_luns(sdev, lun_list, &lun_count);
+
+ DPRINT_INFO(STORVSC_DRV,
+ "report luns on scsi device (%p) found %u luns ",
+ sdev, lun_count);
+ DPRINT_INFO(STORVSC_DRV,
+ "existing luns on scsi device (%p) host (%d)",
+ sdev, host->host_no);
+
+ scsi_device_put(sdev);
+ break;
+ }
+
+ for (i = 0; i < lun_count; i++)
+ DPRINT_INFO(STORVSC_DRV, "%d) lun %u", i, lun_list[i]);
+
+ /* Rescan for devices that may have been removed.
+ * We do not have to worry that new devices may have been added since
+ * this callback is serialized by the workqueue ie add/remove are done
+ * here.
+ */
+ shost_for_each_device(sdev, host) {
+ /* See if this device is still here */
+ found = 0;
+ for (i = 0; i < lun_count; i++) {
+ if (sdev->lun == lun_list[i]) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ DPRINT_INFO(STORVSC_DRV, "lun (%u) does not exists",
+ sdev->lun);
+ sdevs_remove_list[sdevs_count++] = sdev;
+ }
+ }
+
+ /* Now remove the devices */
+ for (i = 0; i < sdevs_count; i++) {
+ DPRINT_INFO(STORVSC_DRV,
+ "removing scsi device (%p) lun (%u)...",
+ sdevs_remove_list[i], sdevs_remove_list[i]->lun);
+
+ /* make sure it is not removed from underneath us */
+ if (!scsi_device_get(sdevs_remove_list[i])) {
+ scsi_remove_device(sdevs_remove_list[i]);
+ scsi_device_put(sdevs_remove_list[i]);
+ }
+ }
+
+ DPRINT_INFO(STORVSC_DRV, "rescan completed on dev obj (%p) "
+ "target (%u) bus (%u)", device_obj,
+ host_device_ctx->target, host_device_ctx->path);
+
+ kfree(lun_list);
+ kfree(sdevs_remove_list);
+
+ DPRINT_EXIT(STORVSC_DRV);
+}
+
+static int storvsc_report_luns(struct scsi_device *sdev, unsigned int luns[],
+ unsigned int *lun_count)
+{
+ int i, j;
+ unsigned int lun = 0;
+ unsigned int num_luns;
+ int result;
+ unsigned char *data;
+ struct scsi_sense_hdr sshdr;
+ unsigned char cmd[16] = {0};
+ /* Add 1 to cover the report_lun header */
+ unsigned int report_len = 8 * (STORVSC_MAX_LUNS_PER_TARGET+1);
+ unsigned long long *report_luns;
+ const unsigned int in_lun_count = *lun_count;
+
+ *lun_count = 0;
+
+ report_luns = kzalloc(report_len, GFP_ATOMIC);
+ if (!report_luns)
+ return -ENOMEM;
+
+ cmd[0] = REPORT_LUNS;
+
+ /* cmd length */
+ *(unsigned int *)&cmd[6] = cpu_to_be32(report_len);
+
+ result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE,
+ (unsigned char *)report_luns, report_len,
+ &sshdr, 30 * HZ, 3, NULL);
+ if (result != 0) {
+ kfree(report_luns);
+ return -EBUSY;
+ }
+
+ /* get the length from the first four bytes */
+ report_len = be32_to_cpu(*(unsigned int *)&report_luns[0]);
+
+ num_luns = (report_len / sizeof(unsigned long long));
+ if (num_luns > in_lun_count) {
+ kfree(report_luns);
+ return -EINVAL;
+ }
+
+ *lun_count = num_luns;
+
+ DPRINT_DBG(STORVSC_DRV,
+ "report luns on scsi device (%p) found %u luns ",
+ sdev, num_luns);
+
+ /* lun id starts at 1 */
+ for (i = 1; i < num_luns + 1; i++) {
+ lun = 0;
+ data = (unsigned char *)&report_luns[i];
+ for (j = 0; j < sizeof(lun); j += 2) {
+ lun = lun | (((data[j] << 8) | data[j + 1]) <<
+ (j * 8));
+ }
+
+ luns[i-1] = lun;
+ }
+
+ kfree(report_luns);
+ return 0;
+}
+
+static void storvsc_host_rescan(struct hv_device *device_obj)
+{
+ struct device_context *device_ctx = to_device_context(device_obj);
+ struct Scsi_Host *host = dev_get_drvdata(&device_ctx->device);
+ struct host_device_context *host_device_ctx;
+
+ DPRINT_ENTER(STORVSC_DRV);
+
+ host_device_ctx = (struct host_device_context *)host->hostdata;
+
+ DPRINT_INFO(STORVSC_DRV, "initiating rescan on dev obj (%p) "
+ "target (%u) bus (%u)...", device_obj,
+ host_device_ctx->target, host_device_ctx->path);
+
+ /*
+ * We need to queue this since the scanning may block and the caller
+ * may be in an intr context
+ */
+ /* scsi_queue_work(host, &host_device_ctx->host_rescan_work); */
+ schedule_work(&host_device_ctx->host_rescan_work);
+ DPRINT_EXIT(STORVSC_DRV);
+}
+
+static int storvsc_get_chs(struct scsi_device *sdev, struct block_device * bdev,
+ sector_t capacity, int *info)
+{
+ sector_t total_sectors = capacity;
+ sector_t cylinder_times_heads = 0;
+ sector_t temp = 0;
+
+ int sectors_per_track = 0;
+ int heads = 0;
+ int cylinders = 0;
+ int rem = 0;
+
+ if (total_sectors > (65535 * 16 * 255))
+ total_sectors = (65535 * 16 * 255);
+
+ if (total_sectors >= (65535 * 16 * 63)) {
+ sectors_per_track = 255;
+ heads = 16;
+
+ cylinder_times_heads = total_sectors;
+ /* sector_div stores the quotient in cylinder_times_heads */
+ rem = sector_div(cylinder_times_heads, sectors_per_track);
+ } else {
+ sectors_per_track = 17;
+
+ cylinder_times_heads = total_sectors;
+ /* sector_div stores the quotient in cylinder_times_heads */
+ rem = sector_div(cylinder_times_heads, sectors_per_track);
+
+ temp = cylinder_times_heads + 1023;
+ /* sector_div stores the quotient in temp */
+ rem = sector_div(temp, 1024);
+
+ heads = temp;
+
+ if (heads < 4)
+ heads = 4;
+
+ if (cylinder_times_heads >= (heads * 1024) || (heads > 16)) {
+ sectors_per_track = 31;
+ heads = 16;
+
+ cylinder_times_heads = total_sectors;
+ /*
+ * sector_div stores the quotient in
+ * cylinder_times_heads
+ */
+ rem = sector_div(cylinder_times_heads,
+ sectors_per_track);
+ }
+
+ if (cylinder_times_heads >= (heads * 1024)) {
+ sectors_per_track = 63;
+ heads = 16;
+
+ cylinder_times_heads = total_sectors;
+ /*
+ * sector_div stores the quotient in
+ * cylinder_times_heads
+ */
+ rem = sector_div(cylinder_times_heads,
+ sectors_per_track);
+ }
+ }
+
+ temp = cylinder_times_heads;
+ /* sector_div stores the quotient in temp */
+ rem = sector_div(temp, heads);
+ cylinders = temp;
+
+ info[0] = heads;
+ info[1] = sectors_per_track;
+ info[2] = cylinders;
+
+ DPRINT_INFO(STORVSC_DRV, "CHS (%d, %d, %d)", cylinders, heads,
+ sectors_per_track);
+
+ return 0;
+}
+
+static int __init storvsc_init(void)
+{
+ int ret;
+
+ DPRINT_ENTER(STORVSC_DRV);
+ DPRINT_INFO(STORVSC_DRV, "Storvsc initializing....");
+ ret = storvsc_drv_init(StorVscInitialize);
+ DPRINT_EXIT(STORVSC_DRV);
+ return ret;
+}
+
+static void __exit storvsc_exit(void)
+{
+ DPRINT_ENTER(STORVSC_DRV);
+ storvsc_drv_exit();
+ DPRINT_ENTER(STORVSC_DRV);
+}
+
+MODULE_LICENSE("GPL");
+module_param(storvsc_ringbuffer_size, int, S_IRUGO);
+module_init(storvsc_init);
+module_exit(storvsc_exit);
diff --git a/drivers/staging/hv/vmbus.h b/drivers/staging/hv/vmbus.h
new file mode 100644
index 000000000000..ae0a896eb392
--- /dev/null
+++ b/drivers/staging/hv/vmbus.h
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#ifndef _VMBUS_H_
+#define _VMBUS_H_
+
+#include <linux/device.h>
+#include "VmbusApi.h"
+
+struct driver_context {
+ struct hv_guid class_id;
+
+ struct device_driver driver;
+
+ /*
+ * Use these methods instead of the struct device_driver so 2.6 kernel
+ * stops complaining
+ * TODO - fix this!
+ */
+ int (*probe)(struct device *);
+ int (*remove)(struct device *);
+ void (*shutdown)(struct device *);
+};
+
+struct device_context {
+ struct work_struct probe_failed_work_item;
+ struct hv_guid class_id;
+ struct hv_guid device_id;
+ int probe_error;
+ struct device device;
+ struct hv_device device_obj;
+};
+
+static inline struct device_context *to_device_context(struct hv_device *d)
+{
+ return container_of(d, struct device_context, device_obj);
+}
+
+static inline struct device_context *device_to_device_context(struct device *d)
+{
+ return container_of(d, struct device_context, device);
+}
+
+static inline struct driver_context *driver_to_driver_context(struct device_driver *d)
+{
+ return container_of(d, struct driver_context, driver);
+}
+
+
+/* Vmbus interface */
+
+int vmbus_child_driver_register(struct driver_context *driver_ctx);
+void vmbus_child_driver_unregister(struct driver_context *driver_ctx);
+void vmbus_get_interface(struct vmbus_channel_interface *interface);
+
+#endif /* _VMBUS_H_ */
diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c
new file mode 100644
index 000000000000..582318f10222
--- /dev/null
+++ b/drivers/staging/hv/vmbus_drv.c
@@ -0,0 +1,999 @@
+/*
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/sysctl.h>
+#include "osd.h"
+#include "logging.h"
+#include "vmbus.h"
+
+
+/* FIXME! We need to do this dynamically for PIC and APIC system */
+#define VMBUS_IRQ 0x5
+#define VMBUS_IRQ_VECTOR IRQ5_VECTOR
+
+/* Main vmbus driver data structure */
+struct vmbus_driver_context {
+ /* !! These must be the first 2 fields !! */
+ /* FIXME, this is a bug */
+ /* The driver field is not used in here. Instead, the bus field is */
+ /* used to represent the driver */
+ struct driver_context drv_ctx;
+ struct vmbus_driver drv_obj;
+
+ struct bus_type bus;
+ struct tasklet_struct msg_dpc;
+ struct tasklet_struct event_dpc;
+
+ /* The bus root device */
+ struct device_context device_ctx;
+};
+
+static int vmbus_match(struct device *device, struct device_driver *driver);
+static int vmbus_probe(struct device *device);
+static int vmbus_remove(struct device *device);
+static void vmbus_shutdown(struct device *device);
+static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env);
+static void vmbus_msg_dpc(unsigned long data);
+static void vmbus_event_dpc(unsigned long data);
+
+static irqreturn_t vmbus_isr(int irq, void *dev_id);
+
+static void vmbus_device_release(struct device *device);
+static void vmbus_bus_release(struct device *device);
+
+static struct hv_device *vmbus_child_device_create(struct hv_guid *type,
+ struct hv_guid *instance,
+ void *context);
+static void vmbus_child_device_destroy(struct hv_device *device_obj);
+static int vmbus_child_device_register(struct hv_device *root_device_obj,
+ struct hv_device *child_device_obj);
+static void vmbus_child_device_unregister(struct hv_device *child_device_obj);
+static void vmbus_child_device_get_info(struct hv_device *device_obj,
+ struct hv_device_info *device_info);
+static ssize_t vmbus_show_device_attr(struct device *dev,
+ struct device_attribute *dev_attr,
+ char *buf);
+
+
+unsigned int vmbus_loglevel = (ALL_MODULES << 16 | INFO_LVL);
+EXPORT_SYMBOL(vmbus_loglevel);
+ /* (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT); */
+ /* (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT); */
+
+static int vmbus_irq = VMBUS_IRQ;
+
+/* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */
+static struct device_attribute vmbus_device_attrs[] = {
+ __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL),
+
+ __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
+
+ __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
+
+ __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
+
+ __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
+ __ATTR_NULL
+};
+
+/* The one and only one */
+static struct vmbus_driver_context g_vmbus_drv = {
+ .bus.name = "vmbus",
+ .bus.match = vmbus_match,
+ .bus.shutdown = vmbus_shutdown,
+ .bus.remove = vmbus_remove,
+ .bus.probe = vmbus_probe,
+ .bus.uevent = vmbus_uevent,
+ .bus.dev_attrs = vmbus_device_attrs,
+};
+
+/**
+ * vmbus_show_device_attr - Show the device attribute in sysfs.
+ *
+ * This is invoked when user does a
+ * "cat /sys/bus/vmbus/devices/<busdevice>/<attr name>"
+ */
+static ssize_t vmbus_show_device_attr(struct device *dev,
+ struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct device_context *device_ctx = device_to_device_context(dev);
+ struct hv_device_info device_info;
+
+ memset(&device_info, 0, sizeof(struct hv_device_info));
+
+ vmbus_child_device_get_info(&device_ctx->device_obj, &device_info);
+
+ if (!strcmp(dev_attr->attr.name, "class_id")) {
+ return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
+ device_info.ChannelType.data[3],
+ device_info.ChannelType.data[2],
+ device_info.ChannelType.data[1],
+ device_info.ChannelType.data[0],
+ device_info.ChannelType.data[5],
+ device_info.ChannelType.data[4],
+ device_info.ChannelType.data[7],
+ device_info.ChannelType.data[6],
+ device_info.ChannelType.data[8],
+ device_info.ChannelType.data[9],
+ device_info.ChannelType.data[10],
+ device_info.ChannelType.data[11],
+ device_info.ChannelType.data[12],
+ device_info.ChannelType.data[13],
+ device_info.ChannelType.data[14],
+ device_info.ChannelType.data[15]);
+ } else if (!strcmp(dev_attr->attr.name, "device_id")) {
+ return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
+ device_info.ChannelInstance.data[3],
+ device_info.ChannelInstance.data[2],
+ device_info.ChannelInstance.data[1],
+ device_info.ChannelInstance.data[0],
+ device_info.ChannelInstance.data[5],
+ device_info.ChannelInstance.data[4],
+ device_info.ChannelInstance.data[7],
+ device_info.ChannelInstance.data[6],
+ device_info.ChannelInstance.data[8],
+ device_info.ChannelInstance.data[9],
+ device_info.ChannelInstance.data[10],
+ device_info.ChannelInstance.data[11],
+ device_info.ChannelInstance.data[12],
+ device_info.ChannelInstance.data[13],
+ device_info.ChannelInstance.data[14],
+ device_info.ChannelInstance.data[15]);
+ } else if (!strcmp(dev_attr->attr.name, "state")) {
+ return sprintf(buf, "%d\n", device_info.ChannelState);
+ } else if (!strcmp(dev_attr->attr.name, "id")) {
+ return sprintf(buf, "%d\n", device_info.ChannelId);
+ } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) {
+ return sprintf(buf, "%d\n", device_info.Outbound.InterruptMask);
+ } else if (!strcmp(dev_attr->attr.name, "out_read_index")) {
+ return sprintf(buf, "%d\n", device_info.Outbound.ReadIndex);
+ } else if (!strcmp(dev_attr->attr.name, "out_write_index")) {
+ return sprintf(buf, "%d\n", device_info.Outbound.WriteIndex);
+ } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) {
+ return sprintf(buf, "%d\n",
+ device_info.Outbound.BytesAvailToRead);
+ } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) {
+ return sprintf(buf, "%d\n",
+ device_info.Outbound.BytesAvailToWrite);
+ } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) {
+ return sprintf(buf, "%d\n", device_info.Inbound.InterruptMask);
+ } else if (!strcmp(dev_attr->attr.name, "in_read_index")) {
+ return sprintf(buf, "%d\n", device_info.Inbound.ReadIndex);
+ } else if (!strcmp(dev_attr->attr.name, "in_write_index")) {
+ return sprintf(buf, "%d\n", device_info.Inbound.WriteIndex);
+ } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) {
+ return sprintf(buf, "%d\n",
+ device_info.Inbound.BytesAvailToRead);
+ } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) {
+ return sprintf(buf, "%d\n",
+ device_info.Inbound.BytesAvailToWrite);
+ } else if (!strcmp(dev_attr->attr.name, "monitor_id")) {
+ return sprintf(buf, "%d\n", device_info.MonitorId);
+ } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) {
+ return sprintf(buf, "%d\n", device_info.ServerMonitorPending);
+ } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) {
+ return sprintf(buf, "%d\n", device_info.ServerMonitorLatency);
+ } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) {
+ return sprintf(buf, "%d\n",
+ device_info.ServerMonitorConnectionId);
+ } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) {
+ return sprintf(buf, "%d\n", device_info.ClientMonitorPending);
+ } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) {
+ return sprintf(buf, "%d\n", device_info.ClientMonitorLatency);
+ } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) {
+ return sprintf(buf, "%d\n",
+ device_info.ClientMonitorConnectionId);
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * vmbus_bus_init -Main vmbus driver initialization routine.
+ *
+ * Here, we
+ * - initialize the vmbus driver context
+ * - setup various driver entry points
+ * - invoke the vmbus hv main init routine
+ * - get the irq resource
+ * - invoke the vmbus to add the vmbus root device
+ * - setup the vmbus root device
+ * - retrieve the channel offers
+ */
+static int vmbus_bus_init(int (*drv_init)(struct hv_driver *drv))
+{
+ struct vmbus_driver_context *vmbus_drv_ctx = &g_vmbus_drv;
+ struct vmbus_driver *vmbus_drv_obj = &g_vmbus_drv.drv_obj;
+ struct device_context *dev_ctx = &g_vmbus_drv.device_ctx;
+ int ret;
+ unsigned int vector;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /*
+ * Set this up to allow lower layer to callback to add/remove child
+ * devices on the bus
+ */
+ vmbus_drv_obj->OnChildDeviceCreate = vmbus_child_device_create;
+ vmbus_drv_obj->OnChildDeviceDestroy = vmbus_child_device_destroy;
+ vmbus_drv_obj->OnChildDeviceAdd = vmbus_child_device_register;
+ vmbus_drv_obj->OnChildDeviceRemove = vmbus_child_device_unregister;
+
+ /* Call to bus driver to initialize */
+ ret = drv_init(&vmbus_drv_obj->Base);
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS_DRV, "Unable to initialize vmbus (%d)", ret);
+ goto cleanup;
+ }
+
+ /* Sanity checks */
+ if (!vmbus_drv_obj->Base.OnDeviceAdd) {
+ DPRINT_ERR(VMBUS_DRV, "OnDeviceAdd() routine not set");
+ ret = -1;
+ goto cleanup;
+ }
+
+ vmbus_drv_ctx->bus.name = vmbus_drv_obj->Base.name;
+
+ /* Initialize the bus context */
+ tasklet_init(&vmbus_drv_ctx->msg_dpc, vmbus_msg_dpc,
+ (unsigned long)vmbus_drv_obj);
+ tasklet_init(&vmbus_drv_ctx->event_dpc, vmbus_event_dpc,
+ (unsigned long)vmbus_drv_obj);
+
+ /* Now, register the bus driver with LDM */
+ ret = bus_register(&vmbus_drv_ctx->bus);
+ if (ret) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* Get the interrupt resource */
+ ret = request_irq(vmbus_irq, vmbus_isr, IRQF_SAMPLE_RANDOM,
+ vmbus_drv_obj->Base.name, NULL);
+
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS_DRV, "ERROR - Unable to request IRQ %d",
+ vmbus_irq);
+
+ bus_unregister(&vmbus_drv_ctx->bus);
+
+ ret = -1;
+ goto cleanup;
+ }
+ vector = VMBUS_IRQ_VECTOR;
+
+ DPRINT_INFO(VMBUS_DRV, "irq 0x%x vector 0x%x", vmbus_irq, vector);
+
+ /* Call to bus driver to add the root device */
+ memset(dev_ctx, 0, sizeof(struct device_context));
+
+ ret = vmbus_drv_obj->Base.OnDeviceAdd(&dev_ctx->device_obj, &vector);
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS_DRV,
+ "ERROR - Unable to add vmbus root device");
+
+ free_irq(vmbus_irq, NULL);
+
+ bus_unregister(&vmbus_drv_ctx->bus);
+
+ ret = -1;
+ goto cleanup;
+ }
+ /* strcpy(dev_ctx->device.bus_id, dev_ctx->device_obj.name); */
+ dev_set_name(&dev_ctx->device, "vmbus_0_0");
+ memcpy(&dev_ctx->class_id, &dev_ctx->device_obj.deviceType,
+ sizeof(struct hv_guid));
+ memcpy(&dev_ctx->device_id, &dev_ctx->device_obj.deviceInstance,
+ sizeof(struct hv_guid));
+
+ /* No need to bind a driver to the root device. */
+ dev_ctx->device.parent = NULL;
+ /* NULL; vmbus_remove() does not get invoked */
+ dev_ctx->device.bus = &vmbus_drv_ctx->bus;
+
+ /* Setup the device dispatch table */
+ dev_ctx->device.release = vmbus_bus_release;
+
+ /* Setup the bus as root device */
+ ret = device_register(&dev_ctx->device);
+ if (ret) {
+ DPRINT_ERR(VMBUS_DRV,
+ "ERROR - Unable to register vmbus root device");
+
+ free_irq(vmbus_irq, NULL);
+ bus_unregister(&vmbus_drv_ctx->bus);
+
+ ret = -1;
+ goto cleanup;
+ }
+
+
+ vmbus_drv_obj->GetChannelOffers();
+
+cleanup:
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return ret;
+}
+
+/**
+ * vmbus_bus_exit - Terminate the vmbus driver.
+ *
+ * This routine is opposite of vmbus_bus_init()
+ */
+static void vmbus_bus_exit(void)
+{
+ struct vmbus_driver *vmbus_drv_obj = &g_vmbus_drv.drv_obj;
+ struct vmbus_driver_context *vmbus_drv_ctx = &g_vmbus_drv;
+
+ struct device_context *dev_ctx = &g_vmbus_drv.device_ctx;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /* Remove the root device */
+ if (vmbus_drv_obj->Base.OnDeviceRemove)
+ vmbus_drv_obj->Base.OnDeviceRemove(&dev_ctx->device_obj);
+
+ if (vmbus_drv_obj->Base.OnCleanup)
+ vmbus_drv_obj->Base.OnCleanup(&vmbus_drv_obj->Base);
+
+ /* Unregister the root bus device */
+ device_unregister(&dev_ctx->device);
+
+ bus_unregister(&vmbus_drv_ctx->bus);
+
+ free_irq(vmbus_irq, NULL);
+
+ tasklet_kill(&vmbus_drv_ctx->msg_dpc);
+ tasklet_kill(&vmbus_drv_ctx->event_dpc);
+
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return;
+}
+
+/**
+ * vmbus_child_driver_register - Register a vmbus's child driver
+ */
+int vmbus_child_driver_register(struct driver_context *driver_ctx)
+{
+ struct vmbus_driver *vmbus_drv_obj = &g_vmbus_drv.drv_obj;
+ int ret;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ DPRINT_INFO(VMBUS_DRV, "child driver (%p) registering - name %s",
+ driver_ctx, driver_ctx->driver.name);
+
+ /* The child driver on this vmbus */
+ driver_ctx->driver.bus = &g_vmbus_drv.bus;
+
+ ret = driver_register(&driver_ctx->driver);
+
+ vmbus_drv_obj->GetChannelOffers();
+
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return ret;
+}
+EXPORT_SYMBOL(vmbus_child_driver_register);
+
+/**
+ * vmbus_child_driver_unregister Unregister a vmbus's child driver
+ */
+void vmbus_child_driver_unregister(struct driver_context *driver_ctx)
+{
+ DPRINT_ENTER(VMBUS_DRV);
+
+ DPRINT_INFO(VMBUS_DRV, "child driver (%p) unregistering - name %s",
+ driver_ctx, driver_ctx->driver.name);
+
+ driver_unregister(&driver_ctx->driver);
+
+ driver_ctx->driver.bus = NULL;
+
+ DPRINT_EXIT(VMBUS_DRV);
+}
+EXPORT_SYMBOL(vmbus_child_driver_unregister);
+
+/**
+ * vmbus_get_interface - Get the vmbus channel interface.
+ *
+ * This is invoked by child/client driver that sits above vmbus
+ */
+void vmbus_get_interface(struct vmbus_channel_interface *interface)
+{
+ struct vmbus_driver *vmbus_drv_obj = &g_vmbus_drv.drv_obj;
+
+ vmbus_drv_obj->GetChannelInterface(interface);
+}
+EXPORT_SYMBOL(vmbus_get_interface);
+
+/**
+ * vmbus_child_device_get_info - Get the vmbus child device info.
+ *
+ * This is invoked to display various device attributes in sysfs.
+ */
+static void vmbus_child_device_get_info(struct hv_device *device_obj,
+ struct hv_device_info *device_info)
+{
+ struct vmbus_driver *vmbus_drv_obj = &g_vmbus_drv.drv_obj;
+
+ vmbus_drv_obj->GetChannelInfo(device_obj, device_info);
+}
+
+/**
+ * vmbus_child_device_create - Creates and registers a new child device on the vmbus.
+ */
+static struct hv_device *vmbus_child_device_create(struct hv_guid *type,
+ struct hv_guid *instance,
+ void *context)
+{
+ struct device_context *child_device_ctx;
+ struct hv_device *child_device_obj;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /* Allocate the new child device */
+ child_device_ctx = kzalloc(sizeof(struct device_context), GFP_KERNEL);
+ if (!child_device_ctx) {
+ DPRINT_ERR(VMBUS_DRV,
+ "unable to allocate device_context for child device");
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return NULL;
+ }
+
+ DPRINT_DBG(VMBUS_DRV, "child device (%p) allocated - "
+ "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x},"
+ "id {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x}",
+ &child_device_ctx->device,
+ type->data[3], type->data[2], type->data[1], type->data[0],
+ type->data[5], type->data[4], type->data[7], type->data[6],
+ type->data[8], type->data[9], type->data[10], type->data[11],
+ type->data[12], type->data[13], type->data[14], type->data[15],
+ instance->data[3], instance->data[2],
+ instance->data[1], instance->data[0],
+ instance->data[5], instance->data[4],
+ instance->data[7], instance->data[6],
+ instance->data[8], instance->data[9],
+ instance->data[10], instance->data[11],
+ instance->data[12], instance->data[13],
+ instance->data[14], instance->data[15]);
+
+ child_device_obj = &child_device_ctx->device_obj;
+ child_device_obj->context = context;
+ memcpy(&child_device_obj->deviceType, &type, sizeof(struct hv_guid));
+ memcpy(&child_device_obj->deviceInstance, &instance,
+ sizeof(struct hv_guid));
+
+ memcpy(&child_device_ctx->class_id, &type, sizeof(struct hv_guid));
+ memcpy(&child_device_ctx->device_id, &instance, sizeof(struct hv_guid));
+
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return child_device_obj;
+}
+
+/**
+ * vmbus_child_device_register - Register the child device on the specified bus
+ */
+static int vmbus_child_device_register(struct hv_device *root_device_obj,
+ struct hv_device *child_device_obj)
+{
+ int ret = 0;
+ struct device_context *root_device_ctx =
+ to_device_context(root_device_obj);
+ struct device_context *child_device_ctx =
+ to_device_context(child_device_obj);
+ static atomic_t device_num = ATOMIC_INIT(0);
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ DPRINT_DBG(VMBUS_DRV, "child device (%p) registering",
+ child_device_ctx);
+
+ /* Make sure we are not registered already */
+ if (strlen(dev_name(&child_device_ctx->device)) != 0) {
+ DPRINT_ERR(VMBUS_DRV,
+ "child device (%p) already registered - busid %s",
+ child_device_ctx,
+ dev_name(&child_device_ctx->device));
+
+ ret = -1;
+ goto Cleanup;
+ }
+
+ /* Set the device bus id. Otherwise, device_register()will fail. */
+ dev_set_name(&child_device_ctx->device, "vmbus_0_%d",
+ atomic_inc_return(&device_num));
+
+ /* The new device belongs to this bus */
+ child_device_ctx->device.bus = &g_vmbus_drv.bus; /* device->dev.bus; */
+ child_device_ctx->device.parent = &root_device_ctx->device;
+ child_device_ctx->device.release = vmbus_device_release;
+
+ /*
+ * Register with the LDM. This will kick off the driver/device
+ * binding...which will eventually call vmbus_match() and vmbus_probe()
+ */
+ ret = device_register(&child_device_ctx->device);
+
+ /* vmbus_probe() error does not get propergate to device_register(). */
+ ret = child_device_ctx->probe_error;
+
+ if (ret)
+ DPRINT_ERR(VMBUS_DRV, "unable to register child device (%p)",
+ &child_device_ctx->device);
+ else
+ DPRINT_INFO(VMBUS_DRV, "child device (%p) registered",
+ &child_device_ctx->device);
+
+Cleanup:
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return ret;
+}
+
+/**
+ * vmbus_child_device_unregister - Remove the specified child device from the vmbus.
+ */
+static void vmbus_child_device_unregister(struct hv_device *device_obj)
+{
+ struct device_context *device_ctx = to_device_context(device_obj);
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ DPRINT_INFO(VMBUS_DRV, "unregistering child device (%p)",
+ &device_ctx->device);
+
+ /*
+ * Kick off the process of unregistering the device.
+ * This will call vmbus_remove() and eventually vmbus_device_release()
+ */
+ device_unregister(&device_ctx->device);
+
+ DPRINT_INFO(VMBUS_DRV, "child device (%p) unregistered",
+ &device_ctx->device);
+
+ DPRINT_EXIT(VMBUS_DRV);
+}
+
+/**
+ * vmbus_child_device_destroy - Destroy the specified child device on the vmbus.
+ */
+static void vmbus_child_device_destroy(struct hv_device *device_obj)
+{
+ DPRINT_ENTER(VMBUS_DRV);
+
+ DPRINT_EXIT(VMBUS_DRV);
+}
+
+/**
+ * vmbus_uevent - add uevent for our device
+ *
+ * This routine is invoked when a device is added or removed on the vmbus to
+ * generate a uevent to udev in the userspace. The udev will then look at its
+ * rule and the uevent generated here to load the appropriate driver
+ */
+static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
+{
+ struct device_context *device_ctx = device_to_device_context(device);
+ int i = 0;
+ int len = 0;
+ int ret;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ DPRINT_INFO(VMBUS_DRV, "generating uevent - VMBUS_DEVICE_CLASS_GUID={"
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x}",
+ device_ctx->class_id.data[3], device_ctx->class_id.data[2],
+ device_ctx->class_id.data[1], device_ctx->class_id.data[0],
+ device_ctx->class_id.data[5], device_ctx->class_id.data[4],
+ device_ctx->class_id.data[7], device_ctx->class_id.data[6],
+ device_ctx->class_id.data[8], device_ctx->class_id.data[9],
+ device_ctx->class_id.data[10],
+ device_ctx->class_id.data[11],
+ device_ctx->class_id.data[12],
+ device_ctx->class_id.data[13],
+ device_ctx->class_id.data[14],
+ device_ctx->class_id.data[15]);
+
+ env->envp_idx = i;
+ env->buflen = len;
+ ret = add_uevent_var(env, "VMBUS_DEVICE_CLASS_GUID={"
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x}",
+ device_ctx->class_id.data[3],
+ device_ctx->class_id.data[2],
+ device_ctx->class_id.data[1],
+ device_ctx->class_id.data[0],
+ device_ctx->class_id.data[5],
+ device_ctx->class_id.data[4],
+ device_ctx->class_id.data[7],
+ device_ctx->class_id.data[6],
+ device_ctx->class_id.data[8],
+ device_ctx->class_id.data[9],
+ device_ctx->class_id.data[10],
+ device_ctx->class_id.data[11],
+ device_ctx->class_id.data[12],
+ device_ctx->class_id.data[13],
+ device_ctx->class_id.data[14],
+ device_ctx->class_id.data[15]);
+
+ if (ret)
+ return ret;
+
+ ret = add_uevent_var(env, "VMBUS_DEVICE_DEVICE_GUID={"
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x%02x%02x}",
+ device_ctx->device_id.data[3],
+ device_ctx->device_id.data[2],
+ device_ctx->device_id.data[1],
+ device_ctx->device_id.data[0],
+ device_ctx->device_id.data[5],
+ device_ctx->device_id.data[4],
+ device_ctx->device_id.data[7],
+ device_ctx->device_id.data[6],
+ device_ctx->device_id.data[8],
+ device_ctx->device_id.data[9],
+ device_ctx->device_id.data[10],
+ device_ctx->device_id.data[11],
+ device_ctx->device_id.data[12],
+ device_ctx->device_id.data[13],
+ device_ctx->device_id.data[14],
+ device_ctx->device_id.data[15]);
+ if (ret)
+ return ret;
+
+ env->envp[env->envp_idx] = NULL;
+
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return 0;
+}
+
+/**
+ * vmbus_match - Attempt to match the specified device to the specified driver
+ */
+static int vmbus_match(struct device *device, struct device_driver *driver)
+{
+ int match = 0;
+ struct driver_context *driver_ctx = driver_to_driver_context(driver);
+ struct device_context *device_ctx = device_to_device_context(device);
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /* We found our driver ? */
+ if (memcmp(&device_ctx->class_id, &driver_ctx->class_id,
+ sizeof(struct hv_guid)) == 0) {
+ /*
+ * !! NOTE: The driver_ctx is not a vmbus_drv_ctx. We typecast
+ * it here to access the struct hv_driver field
+ */
+ struct vmbus_driver_context *vmbus_drv_ctx =
+ (struct vmbus_driver_context *)driver_ctx;
+
+ device_ctx->device_obj.Driver = &vmbus_drv_ctx->drv_obj.Base;
+ DPRINT_INFO(VMBUS_DRV,
+ "device object (%p) set to driver object (%p)",
+ &device_ctx->device_obj,
+ device_ctx->device_obj.Driver);
+
+ match = 1;
+ }
+
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return match;
+}
+
+/**
+ * vmbus_probe_failed_cb - Callback when a driver probe failed in vmbus_probe()
+ *
+ * We need a callback because we cannot invoked device_unregister() inside
+ * vmbus_probe() since vmbus_probe() may be invoked inside device_register()
+ * i.e. we cannot call device_unregister() inside device_register()
+ */
+static void vmbus_probe_failed_cb(struct work_struct *context)
+{
+ struct device_context *device_ctx = (struct device_context *)context;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /*
+ * Kick off the process of unregistering the device.
+ * This will call vmbus_remove() and eventually vmbus_device_release()
+ */
+ device_unregister(&device_ctx->device);
+
+ /* put_device(&device_ctx->device); */
+ DPRINT_EXIT(VMBUS_DRV);
+}
+
+/**
+ * vmbus_probe - Add the new vmbus's child device
+ */
+static int vmbus_probe(struct device *child_device)
+{
+ int ret = 0;
+ struct driver_context *driver_ctx =
+ driver_to_driver_context(child_device->driver);
+ struct device_context *device_ctx =
+ device_to_device_context(child_device);
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /* Let the specific open-source driver handles the probe if it can */
+ if (driver_ctx->probe) {
+ ret = device_ctx->probe_error = driver_ctx->probe(child_device);
+ if (ret != 0) {
+ DPRINT_ERR(VMBUS_DRV, "probe() failed for device %s "
+ "(%p) on driver %s (%d)...",
+ dev_name(child_device), child_device,
+ child_device->driver->name, ret);
+
+ INIT_WORK(&device_ctx->probe_failed_work_item,
+ vmbus_probe_failed_cb);
+ schedule_work(&device_ctx->probe_failed_work_item);
+ }
+ } else {
+ DPRINT_ERR(VMBUS_DRV, "probe() method not set for driver - %s",
+ child_device->driver->name);
+ ret = -1;
+ }
+
+ DPRINT_EXIT(VMBUS_DRV);
+ return ret;
+}
+
+/**
+ * vmbus_remove - Remove a vmbus device
+ */
+static int vmbus_remove(struct device *child_device)
+{
+ int ret;
+ struct driver_context *driver_ctx;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /* Special case root bus device */
+ if (child_device->parent == NULL) {
+ /*
+ * No-op since it is statically defined and handle in
+ * vmbus_bus_exit()
+ */
+ DPRINT_EXIT(VMBUS_DRV);
+ return 0;
+ }
+
+ if (child_device->driver) {
+ driver_ctx = driver_to_driver_context(child_device->driver);
+
+ /*
+ * Let the specific open-source driver handles the removal if
+ * it can
+ */
+ if (driver_ctx->remove) {
+ ret = driver_ctx->remove(child_device);
+ } else {
+ DPRINT_ERR(VMBUS_DRV,
+ "remove() method not set for driver - %s",
+ child_device->driver->name);
+ ret = -1;
+ }
+ }
+
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return 0;
+}
+
+/**
+ * vmbus_shutdown - Shutdown a vmbus device
+ */
+static void vmbus_shutdown(struct device *child_device)
+{
+ struct driver_context *driver_ctx;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /* Special case root bus device */
+ if (child_device->parent == NULL) {
+ /*
+ * No-op since it is statically defined and handle in
+ * vmbus_bus_exit()
+ */
+ DPRINT_EXIT(VMBUS_DRV);
+ return;
+ }
+
+ /* The device may not be attached yet */
+ if (!child_device->driver) {
+ DPRINT_EXIT(VMBUS_DRV);
+ return;
+ }
+
+ driver_ctx = driver_to_driver_context(child_device->driver);
+
+ /* Let the specific open-source driver handles the removal if it can */
+ if (driver_ctx->shutdown)
+ driver_ctx->shutdown(child_device);
+
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return;
+}
+
+/**
+ * vmbus_bus_release - Final callback release of the vmbus root device
+ */
+static void vmbus_bus_release(struct device *device)
+{
+ DPRINT_ENTER(VMBUS_DRV);
+ /* FIXME */
+ /* Empty release functions are a bug, or a major sign
+ * of a problem design, this MUST BE FIXED! */
+ dev_err(device, "%s needs to be fixed!\n", __func__);
+ WARN_ON(1);
+ DPRINT_EXIT(VMBUS_DRV);
+}
+
+/**
+ * vmbus_device_release - Final callback release of the vmbus child device
+ */
+static void vmbus_device_release(struct device *device)
+{
+ struct device_context *device_ctx = device_to_device_context(device);
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ /* vmbus_child_device_destroy(&device_ctx->device_obj); */
+ kfree(device_ctx);
+
+ /* !!DO NOT REFERENCE device_ctx anymore at this point!! */
+ DPRINT_EXIT(VMBUS_DRV);
+
+ return;
+}
+
+/**
+ * vmbus_msg_dpc - Tasklet routine to handle hypervisor messages
+ */
+static void vmbus_msg_dpc(unsigned long data)
+{
+ struct vmbus_driver *vmbus_drv_obj = (struct vmbus_driver *)data;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ ASSERT(vmbus_drv_obj->OnMsgDpc != NULL);
+
+ /* Call to bus driver to handle interrupt */
+ vmbus_drv_obj->OnMsgDpc(&vmbus_drv_obj->Base);
+
+ DPRINT_EXIT(VMBUS_DRV);
+}
+
+/**
+ * vmbus_msg_dpc - Tasklet routine to handle hypervisor events
+ */
+static void vmbus_event_dpc(unsigned long data)
+{
+ struct vmbus_driver *vmbus_drv_obj = (struct vmbus_driver *)data;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ ASSERT(vmbus_drv_obj->OnEventDpc != NULL);
+
+ /* Call to bus driver to handle interrupt */
+ vmbus_drv_obj->OnEventDpc(&vmbus_drv_obj->Base);
+
+ DPRINT_EXIT(VMBUS_DRV);
+}
+
+static irqreturn_t vmbus_isr(int irq, void *dev_id)
+{
+ struct vmbus_driver *vmbus_driver_obj = &g_vmbus_drv.drv_obj;
+ int ret;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ ASSERT(vmbus_driver_obj->OnIsr != NULL);
+
+ /* Call to bus driver to handle interrupt */
+ ret = vmbus_driver_obj->OnIsr(&vmbus_driver_obj->Base);
+
+ /* Schedules a dpc if necessary */
+ if (ret > 0) {
+ if (test_bit(0, (unsigned long *)&ret))
+ tasklet_schedule(&g_vmbus_drv.msg_dpc);
+
+ if (test_bit(1, (unsigned long *)&ret))
+ tasklet_schedule(&g_vmbus_drv.event_dpc);
+
+ DPRINT_EXIT(VMBUS_DRV);
+ return IRQ_HANDLED;
+ } else {
+ DPRINT_EXIT(VMBUS_DRV);
+ return IRQ_NONE;
+ }
+}
+
+static int __init vmbus_init(void)
+{
+ int ret = 0;
+
+ DPRINT_ENTER(VMBUS_DRV);
+
+ DPRINT_INFO(VMBUS_DRV,
+ "Vmbus initializing.... current log level 0x%x (%x,%x)",
+ vmbus_loglevel, HIWORD(vmbus_loglevel), LOWORD(vmbus_loglevel));
+ /* Todo: it is used for loglevel, to be ported to new kernel. */
+
+ ret = vmbus_bus_init(VmbusInitialize);
+
+ DPRINT_EXIT(VMBUS_DRV);
+ return ret;
+}
+
+static void __exit vmbus_exit(void)
+{
+ DPRINT_ENTER(VMBUS_DRV);
+
+ vmbus_bus_exit();
+ /* Todo: it is used for loglevel, to be ported to new kernel. */
+ DPRINT_EXIT(VMBUS_DRV);
+ return;
+}
+
+MODULE_LICENSE("GPL");
+module_param(vmbus_irq, int, S_IRUGO);
+module_param(vmbus_loglevel, int, S_IRUGO);
+
+module_init(vmbus_init);
+module_exit(vmbus_exit);
diff --git a/drivers/staging/hv/vstorage.h b/drivers/staging/hv/vstorage.h
new file mode 100644
index 000000000000..6d160a53914e
--- /dev/null
+++ b/drivers/staging/hv/vstorage.h
@@ -0,0 +1,192 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+/* vstorage.w revision number. This is used in the case of a version match, */
+/* to alert the user that structure sizes may be mismatched even though the */
+/* protocol versions match. */
+
+#define REVISION_STRING(REVISION_) #REVISION_
+#define FILL_VMSTOR_REVISION(RESULT_LVALUE_) \
+{ \
+ char *revisionString = REVISION_STRING($Revision: 6 $) + 11; \
+ RESULT_LVALUE_ = 0; \
+ while (*revisionString >= '0' && *revisionString <= '9') { \
+ RESULT_LVALUE_ *= 10; \
+ RESULT_LVALUE_ += *revisionString - '0'; \
+ revisionString++; \
+ } \
+}
+
+/* Major/minor macros. Minor version is in LSB, meaning that earlier flat */
+/* version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). */
+#define VMSTOR_PROTOCOL_MAJOR(VERSION_) (((VERSION_) >> 8) & 0xff)
+#define VMSTOR_PROTOCOL_MINOR(VERSION_) (((VERSION_)) & 0xff)
+#define VMSTOR_PROTOCOL_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \
+ (((MINOR_) & 0xff)))
+#define VMSTOR_INVALID_PROTOCOL_VERSION (-1)
+
+/* Version history: */
+/* V1 Beta 0.1 */
+/* V1 RC < 2008/1/31 1.0 */
+/* V1 RC > 2008/1/31 2.0 */
+#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0)
+
+
+
+
+/* This will get replaced with the max transfer length that is possible on */
+/* the host adapter. */
+/* The max transfer length will be published when we offer a vmbus channel. */
+#define MAX_TRANSFER_LENGTH 0x40000
+#define DEFAULT_PACKET_SIZE (sizeof(struct vmdata_gpa_direct) + \
+ sizeof(struct vstor_packet) + \
+ sizesizeof(u64) * (MAX_TRANSFER_LENGTH / PAGE_SIZE)))
+
+
+/* Packet structure describing virtual storage requests. */
+enum vstor_packet_operation {
+ VStorOperationCompleteIo = 1,
+ VStorOperationRemoveDevice = 2,
+ VStorOperationExecuteSRB = 3,
+ VStorOperationResetLun = 4,
+ VStorOperationResetAdapter = 5,
+ VStorOperationResetBus = 6,
+ VStorOperationBeginInitialization = 7,
+ VStorOperationEndInitialization = 8,
+ VStorOperationQueryProtocolVersion = 9,
+ VStorOperationQueryProperties = 10,
+ VStorOperationMaximum = 10
+};
+
+/*
+ * Platform neutral description of a scsi request -
+ * this remains the same across the write regardless of 32/64 bit
+ * note: it's patterned off the SCSI_PASS_THROUGH structure
+ */
+#define CDB16GENERIC_LENGTH 0x10
+
+#ifndef SENSE_BUFFER_SIZE
+#define SENSE_BUFFER_SIZE 0x12
+#endif
+
+#define MAX_DATA_BUFFER_LENGTH_WITH_PADDING 0x14
+
+struct vmscsi_request {
+ unsigned short Length;
+ unsigned char SrbStatus;
+ unsigned char ScsiStatus;
+
+ unsigned char PortNumber;
+ unsigned char PathId;
+ unsigned char TargetId;
+ unsigned char Lun;
+
+ unsigned char CdbLength;
+ unsigned char SenseInfoLength;
+ unsigned char DataIn;
+ unsigned char Reserved;
+
+ unsigned int DataTransferLength;
+
+ union {
+ unsigned char Cdb[CDB16GENERIC_LENGTH];
+
+ unsigned char SenseData[SENSE_BUFFER_SIZE];
+
+ unsigned char ReservedArray[MAX_DATA_BUFFER_LENGTH_WITH_PADDING];
+ };
+} __attribute((packed));
+
+
+/*
+ * This structure is sent during the intialization phase to get the different
+ * properties of the channel.
+ */
+struct vmstorage_channel_properties {
+ unsigned short ProtocolVersion;
+ unsigned char PathId;
+ unsigned char TargetId;
+
+ /* Note: port number is only really known on the client side */
+ unsigned int PortNumber;
+ unsigned int Flags;
+ unsigned int MaxTransferBytes;
+
+ /* This id is unique for each channel and will correspond with */
+ /* vendor specific data in the inquirydata */
+ unsigned long long UniqueId;
+} __attribute__((packed));
+
+/* This structure is sent during the storage protocol negotiations. */
+struct vmstorage_protocol_version {
+ /* Major (MSW) and minor (LSW) version numbers. */
+ unsigned short MajorMinor;
+
+ /*
+ * Revision number is auto-incremented whenever this file is changed
+ * (See FILL_VMSTOR_REVISION macro above). Mismatch does not
+ * definitely indicate incompatibility--but it does indicate mismatched
+ * builds.
+ */
+ unsigned short Revision;
+} __attribute__((packed));
+
+/* Channel Property Flags */
+#define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1
+#define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2
+
+struct vstor_packet {
+ /* Requested operation type */
+ enum vstor_packet_operation Operation;
+
+ /* Flags - see below for values */
+ unsigned int Flags;
+
+ /* Status of the request returned from the server side. */
+ unsigned int Status;
+
+ /* Data payload area */
+ union {
+ /*
+ * Structure used to forward SCSI commands from the
+ * client to the server.
+ */
+ struct vmscsi_request VmSrb;
+
+ /* Structure used to query channel properties. */
+ struct vmstorage_channel_properties StorageChannelProperties;
+
+ /* Used during version negotiations. */
+ struct vmstorage_protocol_version Version;
+ };
+} __attribute__((packed));
+
+/* Packet flags */
+/*
+ * This flag indicates that the server should send back a completion for this
+ * packet.
+ */
+#define REQUEST_COMPLETION_FLAG 0x1
+
+/* This is the set of flags that the vsc can set in any packets it sends */
+#define VSC_LEGAL_FLAGS (REQUEST_COMPLETION_FLAG)