Staging: hv: add the Hyper-V virtual bus
authorHank Janssen <hjanssen@microsoft.com>
Mon, 13 Jul 2009 23:02:34 +0000 (16:02 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Sep 2009 19:01:43 +0000 (12:01 -0700)
This is the virtual bus that all of the Linux Hyper-V drivers use.

Signed-off-by: Hank Janssen <hjanssen@microsoft.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
17 files changed:
drivers/staging/hv/Channel.c [new file with mode: 0644]
drivers/staging/hv/Channel.h [new file with mode: 0644]
drivers/staging/hv/ChannelInterface.c [new file with mode: 0644]
drivers/staging/hv/ChannelInterface.h [new file with mode: 0644]
drivers/staging/hv/ChannelMgmt.c [new file with mode: 0644]
drivers/staging/hv/ChannelMgmt.h [new file with mode: 0644]
drivers/staging/hv/Connection.c [new file with mode: 0644]
drivers/staging/hv/Hv.c [new file with mode: 0644]
drivers/staging/hv/Hv.h [new file with mode: 0644]
drivers/staging/hv/RingBuffer.c [new file with mode: 0644]
drivers/staging/hv/RingBuffer.h [new file with mode: 0644]
drivers/staging/hv/Sources.c [new file with mode: 0644]
drivers/staging/hv/VersionInfo.h [new file with mode: 0644]
drivers/staging/hv/Vmbus.c [new file with mode: 0644]
drivers/staging/hv/VmbusPrivate.h [new file with mode: 0644]
drivers/staging/hv/osd.c [new file with mode: 0644]
drivers/staging/hv/vmbus_drv.c [new file with mode: 0644]

diff --git a/drivers/staging/hv/Channel.c b/drivers/staging/hv/Channel.c
new file mode 100644 (file)
index 0000000..0b78604
--- /dev/null
@@ -0,0 +1,1199 @@
+/*
+ *
+ * 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 "osd.h"
+#include "logging.h"
+
+#include "VmbusPrivate.h"
+
+//
+// Internal routines
+//
+static int
+VmbusChannelCreateGpadlHeader(
+       PVOID                                   Kbuffer,        // must be phys and virt contiguous
+       UINT32                                  Size,           // page-size multiple
+       VMBUS_CHANNEL_MSGINFO   **msgInfo,
+       UINT32                                  *MessageCount
+       );
+
+static void
+DumpVmbusChannel(
+       VMBUS_CHANNEL                   *Channel
+       );
+
+
+static void
+VmbusChannelSetEvent(
+       VMBUS_CHANNEL                   *Channel
+       );
+
+
+#if 0
+static void
+DumpMonitorPage(
+       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.AsUINT32);
+                       DPRINT_DBG(VMBUS, "param-flag (%d)(%d) - %d", i, j, MonitorPage->Parameter[i][j].FlagNumber);
+
+               }
+       }
+}
+#endif
+
+/*++
+
+Name:
+       VmbusChannelSetEvent()
+
+Description:
+       Trigger an event notification on the specified channel.
+
+--*/
+static void
+VmbusChannelSetEvent(
+       VMBUS_CHANNEL                   *Channel
+       )
+{
+       HV_MONITOR_PAGE *monitorPage;
+
+       DPRINT_ENTER(VMBUS);
+
+       if (Channel->OfferMsg.MonitorAllocated)
+       {
+               // Each UINT32 represents 32 channels
+               BitSet((UINT32*)gVmbusConnection.SendInterruptPage + (Channel->OfferMsg.ChildRelId >> 5), Channel->OfferMsg.ChildRelId & 31);
+
+               monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages;
+               monitorPage++; // Get the child to parent monitor page
+
+               BitSet((UINT32*) &monitorPage->TriggerGroup[Channel->MonitorGroup].Pending, Channel->MonitorBit);
+       }
+       else
+       {
+               VmbusSetEvent(Channel->OfferMsg.ChildRelId);
+       }
+
+       DPRINT_EXIT(VMBUS);
+}
+
+#if 0
+static void
+VmbusChannelClearEvent(
+       VMBUS_CHANNEL                   *Channel
+       )
+{
+       HV_MONITOR_PAGE *monitorPage;
+
+       DPRINT_ENTER(VMBUS);
+
+       if (Channel->OfferMsg.MonitorAllocated)
+       {
+               // Each UINT32 represents 32 channels
+               BitClear((UINT32*)gVmbusConnection.SendInterruptPage + (Channel->OfferMsg.ChildRelId >> 5), Channel->OfferMsg.ChildRelId & 31);
+
+               monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages;
+               monitorPage++; // Get the child to parent monitor page
+
+               BitClear((UINT32*) &monitorPage->TriggerGroup[Channel->MonitorGroup].Pending, Channel->MonitorBit);
+       }
+
+       DPRINT_EXIT(VMBUS);
+}
+
+#endif
+/*++;
+
+Name:
+       VmbusChannelGetDebugInfo()
+
+Description:
+       Retrieve various channel debug info
+
+--*/
+void
+VmbusChannelGetDebugInfo(
+       VMBUS_CHANNEL                           *Channel,
+       VMBUS_CHANNEL_DEBUG_INFO        *DebugInfo
+       )
+{
+       HV_MONITOR_PAGE *monitorPage;
+    UINT8 monitorGroup    = (UINT8)Channel->OfferMsg.MonitorId / 32;
+    UINT8 monitorOffset   = (UINT8)Channel->OfferMsg.MonitorId % 32;
+       //UINT32 monitorBit     = 1 << monitorOffset;
+
+       DebugInfo->RelId = Channel->OfferMsg.ChildRelId;
+       DebugInfo->State = Channel->State;
+       memcpy(&DebugInfo->InterfaceType, &Channel->OfferMsg.Offer.InterfaceType, sizeof(GUID));
+       memcpy(&DebugInfo->InterfaceInstance, &Channel->OfferMsg.Offer.InterfaceInstance, sizeof(GUID));
+
+       monitorPage = (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);
+}
+
+
+/*++;
+
+Name:
+       VmbusChannelOpen()
+
+Description:
+       Open the specified channel.
+
+--*/
+int
+VmbusChannelOpen(
+       VMBUS_CHANNEL                   *NewChannel,
+       UINT32                                  SendRingBufferSize,
+       UINT32                                  RecvRingBufferSize,
+       PVOID                                   UserData,
+       UINT32                                  UserDataLen,
+       PFN_CHANNEL_CALLBACK    pfnOnChannelCallback,
+       PVOID                                   Context
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL_OPEN_CHANNEL* openMsg;
+       VMBUS_CHANNEL_MSGINFO* openInfo;
+       void *in, *out;
+
+       DPRINT_ENTER(VMBUS);
+
+       // Aligned to page size
+       ASSERT(!(SendRingBufferSize & (PAGE_SIZE -1)));
+       ASSERT(!(RecvRingBufferSize & (PAGE_SIZE -1)));
+
+       NewChannel->OnChannelCallback = pfnOnChannelCallback;
+       NewChannel->ChannelCallbackContext = Context;
+
+       // Allocate the ring buffer
+       out = PageAlloc((SendRingBufferSize + RecvRingBufferSize) >> PAGE_SHIFT);
+       //out = MemAllocZeroed(sendRingBufferSize + recvRingBufferSize);
+       ASSERT(out);
+       ASSERT(((ULONG_PTR)out & (PAGE_SIZE-1)) == 0);
+
+       in = (void*)((ULONG_PTR)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 =
+               (VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_OPEN_CHANNEL));
+       ASSERT(openInfo != NULL);
+
+       openInfo->WaitEvent = WaitEventCreate();
+
+       openMsg = (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);
+       }
+
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+       INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &openInfo->MsgListEntry);
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       DPRINT_DBG(VMBUS, "Sending channel open msg...");
+
+       ret = VmbusPostMessage(openMsg, sizeof(VMBUS_CHANNEL_OPEN_CHANNEL));
+       if (ret != 0)
+       {
+               DPRINT_ERR(VMBUS, "unable to open channel - %d", ret);
+               goto Cleanup;
+       }
+
+       // FIXME: Need to time-out here
+       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:
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+       REMOVE_ENTRY_LIST(&openInfo->MsgListEntry);
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       WaitEventClose(openInfo->WaitEvent);
+       MemFree(openInfo);
+
+       DPRINT_EXIT(VMBUS);
+
+       return 0;
+}
+
+/*++;
+
+Name:
+       DumpGpadlBody()
+
+Description:
+       Dump the gpadl body message to the console for debugging purposes.
+
+--*/
+static void DumpGpadlBody(
+       VMBUS_CHANNEL_GPADL_BODY        *Gpadl,
+       UINT32                                          Len)
+{
+       int i=0;
+       int pfnCount=0;
+
+       pfnCount = (Len - sizeof(VMBUS_CHANNEL_GPADL_BODY))/ sizeof(UINT64);
+       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]);
+       }
+}
+
+
+/*++;
+
+Name:
+       DumpGpadlHeader()
+
+Description:
+       Dump the gpadl header message to the console for debugging purposes.
+
+--*/
+static void DumpGpadlHeader(
+       VMBUS_CHANNEL_GPADL_HEADER      *Gpadl
+       )
+{
+       int i=0,j=0;
+       int pageCount=0;
+
+
+       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]);
+               }
+       }
+}
+
+/*++;
+
+Name:
+       VmbusChannelCreateGpadlHeader()
+
+Description:
+       Creates a gpadl for the specified buffer
+
+--*/
+static int
+VmbusChannelCreateGpadlHeader(
+       PVOID                                   Kbuffer,        // from kmalloc()
+       UINT32                                  Size,           // page-size multiple
+       VMBUS_CHANNEL_MSGINFO   **MsgInfo,
+       UINT32                                  *MessageCount)
+{
+       int i;
+       int pageCount;
+    unsigned long long pfn;
+       VMBUS_CHANNEL_GPADL_HEADER* gpaHeader;
+       VMBUS_CHANNEL_GPADL_BODY* gpadlBody;
+       VMBUS_CHANNEL_MSGINFO* msgHeader;
+       VMBUS_CHANNEL_MSGINFO* msgBody;
+       UINT32                          msgSize;
+
+       int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
+
+       //ASSERT( (kbuffer & (PAGE_SIZE-1)) == 0);
+       ASSERT( (Size & (PAGE_SIZE-1)) == 0);
+
+       pageCount = Size >> PAGE_SHIFT;
+       pfn = GetPhysicalAddress(Kbuffer) >> PAGE_SHIFT;
+
+       // do we need a gpadl body msg
+       pfnSize = MAX_SIZE_CHANNEL_MESSAGE - sizeof(VMBUS_CHANNEL_GPADL_HEADER) - sizeof(GPA_RANGE);
+       pfnCount = pfnSize / sizeof(UINT64);
+
+       if (pageCount > pfnCount) // we need a gpadl body
+       {
+               // fill in the header
+               msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_HEADER) + sizeof(GPA_RANGE) + pfnCount*sizeof(UINT64);
+               msgHeader =  MemAllocZeroed(msgSize);
+
+               INITIALIZE_LIST_HEAD(&msgHeader->SubMsgList);
+               msgHeader->MessageSize=msgSize;
+
+               gpaHeader = (VMBUS_CHANNEL_GPADL_HEADER*)msgHeader->Msg;
+               gpaHeader->RangeCount = 1;
+               gpaHeader->RangeBufLen = sizeof(GPA_RANGE) + pageCount*sizeof(UINT64);
+               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(VMBUS_CHANNEL_GPADL_BODY);
+               pfnCount = pfnSize / sizeof(UINT64);
+
+               // fill in the body
+               while (pfnLeft)
+               {
+                       if (pfnLeft > pfnCount)
+                       {
+                               pfnCurr = pfnCount;
+                       }
+                       else
+                       {
+                               pfnCurr = pfnLeft;
+                       }
+
+                       msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_BODY) + pfnCurr*sizeof(UINT64);
+                       msgBody =  MemAllocZeroed(msgSize);
+                       ASSERT(msgBody);
+                       msgBody->MessageSize = msgSize;
+                       (*MessageCount)++;
+                       gpadlBody = (VMBUS_CHANNEL_GPADL_BODY*)msgBody->Msg;
+
+                       // FIXME: Gpadl is UINT32 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
+                       INSERT_TAIL_LIST(&msgHeader->SubMsgList, &msgBody->MsgListEntry);
+                       pfnSum += pfnCurr;
+                       pfnLeft -= pfnCurr;
+               }
+       }
+       else
+       {
+               // everything fits in a header
+               msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_HEADER) + sizeof(GPA_RANGE) + pageCount*sizeof(UINT64);
+               msgHeader =  MemAllocZeroed(msgSize);
+               msgHeader->MessageSize=msgSize;
+
+               gpaHeader = (VMBUS_CHANNEL_GPADL_HEADER*)msgHeader->Msg;
+               gpaHeader->RangeCount = 1;
+               gpaHeader->RangeBufLen = sizeof(GPA_RANGE) + pageCount*sizeof(UINT64);
+               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;
+}
+
+
+/*++;
+
+Name:
+       VmbusChannelEstablishGpadl()
+
+Description:
+       Estabish a GPADL for the specified buffer
+
+--*/
+int
+VmbusChannelEstablishGpadl(
+       VMBUS_CHANNEL   *Channel,
+       PVOID                   Kbuffer,        // from kmalloc()
+       UINT32                  Size,           // page-size multiple
+       UINT32                  *GpadlHandle
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL_GPADL_HEADER* gpadlMsg;
+       VMBUS_CHANNEL_GPADL_BODY* gpadlBody;
+       //VMBUS_CHANNEL_GPADL_CREATED* gpadlCreated;
+
+       VMBUS_CHANNEL_MSGINFO *msgInfo;
+       VMBUS_CHANNEL_MSGINFO *subMsgInfo;
+
+       UINT32 msgCount;
+       LIST_ENTRY* anchor;
+       LIST_ENTRY* curr;
+       UINT32 nextGpadlHandle;
+
+       DPRINT_ENTER(VMBUS);
+
+       nextGpadlHandle = gVmbusConnection.NextGpadlHandle;
+       InterlockedIncrement((int*)&gVmbusConnection.NextGpadlHandle);
+
+       VmbusChannelCreateGpadlHeader(Kbuffer, Size, &msgInfo, &msgCount);
+       ASSERT(msgInfo != NULL);
+       ASSERT(msgCount >0);
+
+       msgInfo->WaitEvent = WaitEventCreate();
+       gpadlMsg = (VMBUS_CHANNEL_GPADL_HEADER*)msgInfo->Msg;
+       gpadlMsg->Header.MessageType = ChannelMessageGpadlHeader;
+       gpadlMsg->ChildRelId = Channel->OfferMsg.ChildRelId;
+       gpadlMsg->Gpadl = nextGpadlHandle;
+
+       DumpGpadlHeader(gpadlMsg);
+
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+       INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry);
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       DPRINT_DBG(VMBUS, "buffer %p, size %d msg cnt %d", Kbuffer, Size, msgCount);
+
+       DPRINT_DBG(VMBUS, "Sending GPADL Header - len %d", msgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
+
+       ret = VmbusPostMessage(gpadlMsg, msgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
+       if (ret != 0)
+       {
+               DPRINT_ERR(VMBUS, "Unable to open channel - %d", ret);
+               goto Cleanup;
+       }
+
+       if (msgCount>1)
+       {
+               ITERATE_LIST_ENTRIES(anchor, curr, &msgInfo->SubMsgList)
+               {
+                       subMsgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
+                       gpadlBody = (VMBUS_CHANNEL_GPADL_BODY*)subMsgInfo->Msg;
+
+                       gpadlBody->Header.MessageType = ChannelMessageGpadlBody;
+                       gpadlBody->Gpadl = nextGpadlHandle;
+
+                       DPRINT_DBG(VMBUS, "Sending GPADL Body - len %d", subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
+
+                       DumpGpadlBody(gpadlBody, subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
+                       ret = VmbusPostMessage(gpadlBody, subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
+                       ASSERT(ret == 0);
+               }
+       }
+       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:
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+       REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       WaitEventClose(msgInfo->WaitEvent);
+       MemFree(msgInfo);
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+
+
+/*++;
+
+Name:
+       VmbusChannelTeardownGpadl()
+
+Description:
+       Teardown the specified GPADL handle
+
+--*/
+int
+VmbusChannelTeardownGpadl(
+       VMBUS_CHANNEL   *Channel,
+       UINT32                  GpadlHandle
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL_GPADL_TEARDOWN *msg;
+       VMBUS_CHANNEL_MSGINFO* info;
+
+       DPRINT_ENTER(VMBUS);
+
+       ASSERT(GpadlHandle != 0);
+
+       info =
+               (VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_TEARDOWN));
+       ASSERT(info != NULL);
+
+       info->WaitEvent = WaitEventCreate();
+
+       msg = (VMBUS_CHANNEL_GPADL_TEARDOWN*)info->Msg;
+
+       msg->Header.MessageType = ChannelMessageGpadlTeardown;
+    msg->ChildRelId  = Channel->OfferMsg.ChildRelId;
+    msg->Gpadl       = GpadlHandle;
+
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+       INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &info->MsgListEntry);
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_GPADL_TEARDOWN));
+       if (ret != 0)
+       {
+               // TODO:
+       }
+
+       WaitEventWait(info->WaitEvent);
+
+       // Received a torndown response
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+       REMOVE_ENTRY_LIST(&info->MsgListEntry);
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       WaitEventClose(info->WaitEvent);
+       MemFree(info);
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+
+/*++
+
+Name:
+       VmbusChannelClose()
+
+Description:
+       Close the specified channel
+
+--*/
+VOID
+VmbusChannelClose(
+       VMBUS_CHANNEL   *Channel
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL_CLOSE_CHANNEL* msg;
+       VMBUS_CHANNEL_MSGINFO* info;
+
+       DPRINT_ENTER(VMBUS);
+
+       // Stop callback and cancel the timer asap
+       Channel->OnChannelCallback = NULL;
+       TimerStop(Channel->PollTimer);
+
+       // Send a closing message
+       info =
+               (VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_CLOSE_CHANNEL));
+       ASSERT(info != NULL);
+
+       //info->waitEvent = WaitEventCreate();
+
+       msg = (VMBUS_CHANNEL_CLOSE_CHANNEL*)info->Msg;
+       msg->Header.MessageType                         = ChannelMessageCloseChannel;
+    msg->ChildRelId                                            = Channel->OfferMsg.ChildRelId;
+
+       ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_CLOSE_CHANNEL));
+       if (ret != 0)
+       {
+               // TODO:
+       }
+
+       // 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);
+
+       PageFree(Channel->RingBufferPages, Channel->RingBufferPageCount);
+
+       MemFree(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)
+       {
+               SpinlockAcquire(gVmbusConnection.ChannelLock);
+               REMOVE_ENTRY_LIST(&Channel->ListEntry);
+               SpinlockRelease(gVmbusConnection.ChannelLock);
+
+               FreeVmbusChannel(Channel);
+       }
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelSendPacket()
+
+Description:
+       Send the specified buffer on the given channel
+
+--*/
+int
+VmbusChannelSendPacket(
+       VMBUS_CHANNEL           *Channel,
+       const PVOID                     Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId,
+       VMBUS_PACKET_TYPE       Type,
+       UINT32                          Flags
+)
+{
+       int ret=0;
+       VMPACKET_DESCRIPTOR desc;
+       UINT32 packetLen = sizeof(VMPACKET_DESCRIPTOR) + BufferLen;
+       UINT32 packetLenAligned = ALIGN_UP(packetLen, sizeof(UINT64));
+       SG_BUFFER_LIST bufferList[3];
+       UINT64 alignedData=0;
+
+       DPRINT_ENTER(VMBUS);
+       DPRINT_DBG(VMBUS, "channel %p buffer %p len %d", Channel, Buffer, BufferLen);
+
+       DumpVmbusChannel(Channel);
+
+       ASSERT((packetLenAligned - packetLen) < sizeof(UINT64));
+
+       // Setup the descriptor
+       desc.Type = Type;//VmbusPacketTypeDataInBand;
+       desc.Flags = Flags;//VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+    desc.DataOffset8 = sizeof(VMPACKET_DESCRIPTOR) >> 3; // in 8-bytes granularity
+    desc.Length8 = (UINT16)(packetLenAligned >> 3);
+    desc.TransactionId = RequestId;
+
+       bufferList[0].Data = &desc;
+       bufferList[0].Length = sizeof(VMPACKET_DESCRIPTOR);
+
+       bufferList[1].Data = Buffer;
+       bufferList[1].Length = BufferLen;
+
+       bufferList[2].Data = &alignedData;
+       bufferList[2].Length = 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;
+}
+
+
+/*++
+
+Name:
+       VmbusChannelSendPacketPageBuffer()
+
+Description:
+       Send a range of single-page buffer packets using a GPADL Direct packet type.
+
+--*/
+int
+VmbusChannelSendPacketPageBuffer(
+       VMBUS_CHANNEL           *Channel,
+       PAGE_BUFFER                     PageBuffers[],
+       UINT32                          PageCount,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId
+)
+{
+       int ret=0;
+       int i=0;
+       VMBUS_CHANNEL_PACKET_PAGE_BUFFER desc;
+       UINT32 descSize;
+       UINT32 packetLen;
+       UINT32 packetLenAligned;
+       SG_BUFFER_LIST bufferList[3];
+       UINT64 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(VMBUS_CHANNEL_PACKET_PAGE_BUFFER) - ((MAX_PAGE_BUFFER_COUNT - PageCount)*sizeof(PAGE_BUFFER));
+       packetLen = descSize + BufferLen;
+       packetLenAligned = ALIGN_UP(packetLen, sizeof(UINT64));
+
+       ASSERT((packetLenAligned - packetLen) < sizeof(UINT64));
+
+       // Setup the descriptor
+       desc.Type = VmbusPacketTypeDataUsingGpaDirect;
+       desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+    desc.DataOffset8 = descSize >> 3; // in 8-bytes grandularity
+    desc.Length8 = (UINT16)(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;
+       }
+
+       bufferList[0].Data = &desc;
+       bufferList[0].Length = descSize;
+
+       bufferList[1].Data = Buffer;
+       bufferList[1].Length = BufferLen;
+
+       bufferList[2].Data = &alignedData;
+       bufferList[2].Length = 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;
+}
+
+
+
+/*++
+
+Name:
+       VmbusChannelSendPacketMultiPageBuffer()
+
+Description:
+       Send a multi-page buffer packet using a GPADL Direct packet type.
+
+--*/
+int
+VmbusChannelSendPacketMultiPageBuffer(
+       VMBUS_CHANNEL           *Channel,
+       MULTIPAGE_BUFFER        *MultiPageBuffer,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId
+)
+{
+       int ret=0;
+       VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER desc;
+       UINT32 descSize;
+       UINT32 packetLen;
+       UINT32 packetLenAligned;
+       SG_BUFFER_LIST bufferList[3];
+       UINT64 alignedData=0;
+       UINT32 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(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER) - ((MAX_MULTIPAGE_BUFFER_COUNT - PfnCount)*sizeof(UINT64));
+       packetLen = descSize + BufferLen;
+       packetLenAligned = ALIGN_UP(packetLen, sizeof(UINT64));
+
+       ASSERT((packetLenAligned - packetLen) < sizeof(UINT64));
+
+       // Setup the descriptor
+       desc.Type = VmbusPacketTypeDataUsingGpaDirect;
+       desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+    desc.DataOffset8 = descSize >> 3; // in 8-bytes grandularity
+    desc.Length8 = (UINT16)(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(UINT64));
+
+       bufferList[0].Data = &desc;
+       bufferList[0].Length = descSize;
+
+       bufferList[1].Data = Buffer;
+       bufferList[1].Length = BufferLen;
+
+       bufferList[2].Data = &alignedData;
+       bufferList[2].Length = 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;
+}
+
+
+/*++
+
+Name:
+       VmbusChannelRecvPacket()
+
+Description:
+       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(
+       VMBUS_CHANNEL           *Channel,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32*                         BufferActualLen,
+       UINT64*                         RequestId
+       )
+{
+       VMPACKET_DESCRIPTOR desc;
+       UINT32 packetLen;
+       UINT32 userLen;
+       int ret;
+
+       DPRINT_ENTER(VMBUS);
+
+       *BufferActualLen = 0;
+       *RequestId = 0;
+
+       SpinlockAcquire(Channel->InboundLock);
+
+       ret = RingBufferPeek(&Channel->Inbound, &desc, sizeof(VMPACKET_DESCRIPTOR));
+       if (ret != 0)
+       {
+               SpinlockRelease(Channel->InboundLock);
+
+               //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)
+       {
+               SpinlockRelease(Channel->InboundLock);
+
+               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));
+
+       SpinlockRelease(Channel->InboundLock);
+
+       DPRINT_EXIT(VMBUS);
+
+       return 0;
+}
+
+/*++
+
+Name:
+       VmbusChannelRecvPacketRaw()
+
+Description:
+       Retrieve the raw packet on the specified channel
+
+--*/
+int
+VmbusChannelRecvPacketRaw(
+       VMBUS_CHANNEL           *Channel,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32*                         BufferActualLen,
+       UINT64*                         RequestId
+       )
+{
+       VMPACKET_DESCRIPTOR desc;
+       UINT32 packetLen;
+       UINT32 userLen;
+       int ret;
+
+       DPRINT_ENTER(VMBUS);
+
+       *BufferActualLen = 0;
+       *RequestId = 0;
+
+       SpinlockAcquire(Channel->InboundLock);
+
+       ret = RingBufferPeek(&Channel->Inbound, &desc, sizeof(VMPACKET_DESCRIPTOR));
+       if (ret != 0)
+       {
+               SpinlockRelease(Channel->InboundLock);
+
+               //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)
+       {
+               SpinlockRelease(Channel->InboundLock);
+
+               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);
+
+       SpinlockRelease(Channel->InboundLock);
+
+       DPRINT_EXIT(VMBUS);
+
+       return 0;
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnChannelEvent()
+
+Description:
+       Channel event callback
+
+--*/
+void
+VmbusChannelOnChannelEvent(
+       VMBUS_CHANNEL           *Channel
+       )
+{
+       DumpVmbusChannel(Channel);
+       ASSERT(Channel->OnChannelCallback);
+#ifdef ENABLE_POLLING
+       TimerStop(Channel->PollTimer);
+       Channel->OnChannelCallback(Channel->ChannelCallbackContext);
+       TimerStart(Channel->PollTimer, 100 /* 100us */);
+#else
+       Channel->OnChannelCallback(Channel->ChannelCallbackContext);
+#endif
+}
+
+/*++
+
+Name:
+       VmbusChannelOnTimer()
+
+Description:
+       Timer event callback
+
+--*/
+void
+VmbusChannelOnTimer(
+       void            *Context
+       )
+{
+       VMBUS_CHANNEL *channel = (VMBUS_CHANNEL*)Context;
+
+       if (channel->OnChannelCallback)
+       {
+               channel->OnChannelCallback(channel->ChannelCallbackContext);
+#ifdef ENABLE_POLLING
+               TimerStart(channel->PollTimer, 100 /* 100us */);
+#endif
+       }
+}
+
+
+/*++
+
+Name:
+       DumpVmbusChannel()
+
+Description:
+       Dump vmbus channel info to the console
+
+--*/
+static void
+DumpVmbusChannel(
+       VMBUS_CHANNEL           *Channel
+       )
+{
+       DPRINT_DBG(VMBUS, "Channel (%d)", Channel->OfferMsg.ChildRelId);
+       DumpRingInfo(&Channel->Outbound, "Outbound ");
+       DumpRingInfo(&Channel->Inbound, "Inbound ");
+}
+
+
+// eof
diff --git a/drivers/staging/hv/Channel.h b/drivers/staging/hv/Channel.h
new file mode 100644 (file)
index 0000000..117b2e1
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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 "osd.h"
+#include "ChannelMgmt.h"
+
+#pragma pack(push,1)
+
+
+// The format must be the same as VMDATA_GPA_DIRECT
+typedef struct _VMBUS_CHANNEL_PACKET_PAGE_BUFFER {
+    UINT16                             Type;
+    UINT16                             DataOffset8;
+    UINT16                             Length8;
+    UINT16                             Flags;
+    UINT64                             TransactionId;
+       UINT32                          Reserved;
+       UINT32                          RangeCount;
+    PAGE_BUFFER                        Range[MAX_PAGE_BUFFER_COUNT];
+} VMBUS_CHANNEL_PACKET_PAGE_BUFFER;
+
+
+// The format must be the same as VMDATA_GPA_DIRECT
+typedef struct _VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER {
+    UINT16                             Type;
+    UINT16                             DataOffset8;
+    UINT16                             Length8;
+    UINT16                             Flags;
+    UINT64                             TransactionId;
+       UINT32                          Reserved;
+       UINT32                          RangeCount;             // Always 1 in this case
+       MULTIPAGE_BUFFER        Range;
+} VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER;
+
+#pragma pack(pop)
+
+//
+// Routines
+//
+
+INTERNAL int
+VmbusChannelOpen(
+       VMBUS_CHANNEL                   *Channel,
+       UINT32                                  SendRingBufferSize,
+       UINT32                                  RecvRingBufferSize,
+       PVOID                                   UserData,
+       UINT32                                  UserDataLen,
+       PFN_CHANNEL_CALLBACK    pfnOnChannelCallback,
+       PVOID                                   Context
+       );
+
+INTERNAL void
+VmbusChannelClose(
+       VMBUS_CHANNEL           *Channel
+       );
+
+INTERNAL int
+VmbusChannelSendPacket(
+       VMBUS_CHANNEL           *Channel,
+       const PVOID                     Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId,
+       VMBUS_PACKET_TYPE       Type,
+       UINT32                          Flags
+);
+
+INTERNAL int
+VmbusChannelSendPacketPageBuffer(
+       VMBUS_CHANNEL           *Channel,
+       PAGE_BUFFER                     PageBuffers[],
+       UINT32                          PageCount,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId
+       );
+
+INTERNAL int
+VmbusChannelSendPacketMultiPageBuffer(
+       VMBUS_CHANNEL           *Channel,
+       MULTIPAGE_BUFFER        *MultiPageBuffer,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId
+);
+
+INTERNAL int
+VmbusChannelEstablishGpadl(
+       VMBUS_CHANNEL           *Channel,
+       PVOID                           Kbuffer,        // from kmalloc()
+       UINT32                          Size,           // page-size multiple
+       UINT32                          *GpadlHandle
+       );
+
+INTERNAL int
+VmbusChannelTeardownGpadl(
+       VMBUS_CHANNEL   *Channel,
+       UINT32                  GpadlHandle
+       );
+
+INTERNAL int
+VmbusChannelRecvPacket(
+       VMBUS_CHANNEL           *Channel,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32*                         BufferActualLen,
+       UINT64*                         RequestId
+       );
+
+INTERNAL int
+VmbusChannelRecvPacketRaw(
+       VMBUS_CHANNEL           *Channel,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32*                         BufferActualLen,
+       UINT64*                         RequestId
+       );
+
+INTERNAL void
+VmbusChannelOnChannelEvent(
+       VMBUS_CHANNEL           *Channel
+       );
+
+INTERNAL void
+VmbusChannelGetDebugInfo(
+       VMBUS_CHANNEL                           *Channel,
+       VMBUS_CHANNEL_DEBUG_INFO        *DebugInfo
+       );
+
+INTERNAL void
+VmbusChannelOnTimer(
+       void            *Context
+       );
+#endif //_CHANNEL_H_
diff --git a/drivers/staging/hv/ChannelInterface.c b/drivers/staging/hv/ChannelInterface.c
new file mode 100644 (file)
index 0000000..1a7663e
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ *
+ * 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 "VmbusPrivate.h"
+
+INTERNAL int
+IVmbusChannelOpen(
+       PDEVICE_OBJECT          Device,
+       UINT32                          SendBufferSize,
+       UINT32                          RecvRingBufferSize,
+       PVOID                           UserData,
+       UINT32                          UserDataLen,
+       VMBUS_CHANNEL_CALLBACK ChannelCallback,
+       PVOID                           Context
+       )
+{
+       return VmbusChannelOpen( (VMBUS_CHANNEL*)Device->context,
+                                                               SendBufferSize,
+                                                               RecvRingBufferSize,
+                                                               UserData,
+                                                               UserDataLen,
+                                                               ChannelCallback,
+                                                               Context);
+}
+
+
+INTERNAL void
+IVmbusChannelClose(
+       PDEVICE_OBJECT          Device
+       )
+{
+       VmbusChannelClose((VMBUS_CHANNEL*)Device->context);
+}
+
+
+INTERNAL int
+IVmbusChannelSendPacket(
+       PDEVICE_OBJECT          Device,
+       const PVOID                     Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId,
+       UINT32                          Type,
+       UINT32                          Flags
+       )
+{
+       return VmbusChannelSendPacket((VMBUS_CHANNEL*)Device->context,
+                                                                       Buffer,
+                                                                       BufferLen,
+                                                                       RequestId,
+                                                                       Type,
+                                                                       Flags);
+}
+
+INTERNAL int
+IVmbusChannelSendPacketPageBuffer(
+       PDEVICE_OBJECT          Device,
+       PAGE_BUFFER                     PageBuffers[],
+       UINT32                          PageCount,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId
+       )
+{
+       return VmbusChannelSendPacketPageBuffer((VMBUS_CHANNEL*)Device->context,
+                                                                                               PageBuffers,
+                                                                                               PageCount,
+                                                                                               Buffer,
+                                                                                               BufferLen,
+                                                                                               RequestId);
+}
+
+INTERNAL int
+IVmbusChannelSendPacketMultiPageBuffer(
+       PDEVICE_OBJECT          Device,
+       MULTIPAGE_BUFFER        *MultiPageBuffer,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT64                          RequestId
+       )
+{
+       return VmbusChannelSendPacketMultiPageBuffer((VMBUS_CHANNEL*)Device->context,
+                                                                                                       MultiPageBuffer,
+                                                                                                       Buffer,
+                                                                                                       BufferLen,
+                                                                                                       RequestId);
+}
+
+INTERNAL int
+IVmbusChannelRecvPacket (
+       PDEVICE_OBJECT          Device,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32*                         BufferActualLen,
+       UINT64*                         RequestId
+       )
+{
+       return VmbusChannelRecvPacket((VMBUS_CHANNEL*)Device->context,
+                                                                       Buffer,
+                                                                       BufferLen,
+                                                                       BufferActualLen,
+                                                                       RequestId);
+}
+
+INTERNAL int
+IVmbusChannelRecvPacketRaw(
+       PDEVICE_OBJECT          Device,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32*                         BufferActualLen,
+       UINT64*                         RequestId
+       )
+{
+       return VmbusChannelRecvPacketRaw((VMBUS_CHANNEL*)Device->context,
+                                                                               Buffer,
+                                                                               BufferLen,
+                                                                               BufferActualLen,
+                                                                               RequestId);
+}
+
+INTERNAL int
+IVmbusChannelEstablishGpadl(
+       PDEVICE_OBJECT          Device,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32*                         GpadlHandle
+       )
+{
+       return VmbusChannelEstablishGpadl((VMBUS_CHANNEL*)Device->context,
+                                                                               Buffer,
+                                                                               BufferLen,
+                                                                               GpadlHandle);
+}
+
+INTERNAL int
+IVmbusChannelTeardownGpadl(
+   PDEVICE_OBJECT              Device,
+   UINT32                              GpadlHandle
+       )
+{
+       return VmbusChannelTeardownGpadl((VMBUS_CHANNEL*)Device->context,
+                                                                               GpadlHandle);
+
+}
+
+INTERNAL void
+GetChannelInterface(
+       VMBUS_CHANNEL_INTERFACE *ChannelInterface
+       )
+{
+       ChannelInterface->Open                                          = IVmbusChannelOpen;
+       ChannelInterface->Close                                         = IVmbusChannelClose;
+       ChannelInterface->SendPacket                            = IVmbusChannelSendPacket;
+       ChannelInterface->SendPacketPageBuffer          = IVmbusChannelSendPacketPageBuffer;
+       ChannelInterface->SendPacketMultiPageBuffer = IVmbusChannelSendPacketMultiPageBuffer;
+       ChannelInterface->RecvPacket                            = IVmbusChannelRecvPacket;
+       ChannelInterface->RecvPacketRaw                         = IVmbusChannelRecvPacketRaw;
+       ChannelInterface->EstablishGpadl                        = IVmbusChannelEstablishGpadl;
+       ChannelInterface->TeardownGpadl                         = IVmbusChannelTeardownGpadl;
+       ChannelInterface->GetInfo                                       = GetChannelInfo;
+}
+
+
+INTERNAL void
+GetChannelInfo(
+       PDEVICE_OBJECT          Device,
+       DEVICE_INFO                     *DeviceInfo
+                          )
+{
+       VMBUS_CHANNEL_DEBUG_INFO debugInfo;
+
+       if (Device->context)
+       {
+               VmbusChannelGetDebugInfo((VMBUS_CHANNEL*)Device->context, &debugInfo);
+
+               DeviceInfo->ChannelId = debugInfo.RelId;
+               DeviceInfo->ChannelState = debugInfo.State;
+               memcpy(&DeviceInfo->ChannelType, &debugInfo.InterfaceType, sizeof(GUID));
+               memcpy(&DeviceInfo->ChannelInstance, &debugInfo.InterfaceInstance, sizeof(GUID));
+
+               DeviceInfo->MonitorId = debugInfo.MonitorId;
+
+               DeviceInfo->ServerMonitorPending = debugInfo.ServerMonitorPending;
+               DeviceInfo->ServerMonitorLatency = debugInfo.ServerMonitorLatency;
+               DeviceInfo->ServerMonitorConnectionId = debugInfo.ServerMonitorConnectionId;
+
+               DeviceInfo->ClientMonitorPending = debugInfo.ClientMonitorPending;
+               DeviceInfo->ClientMonitorLatency = debugInfo.ClientMonitorLatency;
+               DeviceInfo->ClientMonitorConnectionId = debugInfo.ClientMonitorConnectionId;
+
+               DeviceInfo->Inbound.InterruptMask = debugInfo.Inbound.CurrentInterruptMask;
+               DeviceInfo->Inbound.ReadIndex = debugInfo.Inbound.CurrentReadIndex;
+               DeviceInfo->Inbound.WriteIndex = debugInfo.Inbound.CurrentWriteIndex;
+               DeviceInfo->Inbound.BytesAvailToRead = debugInfo.Inbound.BytesAvailToRead;
+               DeviceInfo->Inbound.BytesAvailToWrite = debugInfo.Inbound.BytesAvailToWrite;
+
+               DeviceInfo->Outbound.InterruptMask = debugInfo.Outbound.CurrentInterruptMask;
+               DeviceInfo->Outbound.ReadIndex = debugInfo.Outbound.CurrentReadIndex;
+               DeviceInfo->Outbound.WriteIndex = debugInfo.Outbound.CurrentWriteIndex;
+               DeviceInfo->Outbound.BytesAvailToRead = debugInfo.Outbound.BytesAvailToRead;
+               DeviceInfo->Outbound.BytesAvailToWrite = debugInfo.Outbound.BytesAvailToWrite;
+       }
+}
diff --git a/drivers/staging/hv/ChannelInterface.h b/drivers/staging/hv/ChannelInterface.h
new file mode 100644 (file)
index 0000000..8f5a4a9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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"
+
+INTERNAL void
+GetChannelInterface(
+       VMBUS_CHANNEL_INTERFACE *ChannelInterface
+       );
+
+INTERNAL void
+GetChannelInfo(
+       PDEVICE_OBJECT          Device,
+       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 (file)
index 0000000..c058d53
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ *
+ * 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 "osd.h"
+#include "logging.h"
+
+#include "VmbusPrivate.h"
+
+//
+// Defines
+//
+
+//
+// Data types
+//
+
+typedef void (*PFN_CHANNEL_MESSAGE_HANDLER)(VMBUS_CHANNEL_MESSAGE_HEADER* msg);
+
+typedef struct _VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY {
+       VMBUS_CHANNEL_MESSAGE_TYPE      messageType;
+       PFN_CHANNEL_MESSAGE_HANDLER messageHandler;
+} VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY;
+
+//
+// Internal routines
+//
+
+static void
+VmbusChannelOnOffer(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       );
+static void
+VmbusChannelOnOpenResult(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       );
+
+static void
+VmbusChannelOnOfferRescind(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       );
+
+static void
+VmbusChannelOnGpadlCreated(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       );
+
+static void
+VmbusChannelOnGpadlTorndown(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       );
+
+static void
+VmbusChannelOnOffersDelivered(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       );
+
+static void
+VmbusChannelOnVersionResponse(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       );
+
+static void
+VmbusChannelProcessOffer(
+       PVOID context
+       );
+
+static void
+VmbusChannelProcessRescindOffer(
+       PVOID context
+       );
+
+
+//
+// Globals
+//
+
+#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 4
+
+const GUID gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED]= {
+       //{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}
+       {.Data  = {0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f}},// Storage - SCSI
+       //{F8615163-DF3E-46c5-913F-F2D2F965ED0E}
+       {.Data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}},     // Network
+       //{CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A}
+       {.Data = {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A}}, // Input
+       //{32412632-86cb-44a2-9b5c-50d1417354f5}
+       {.Data = {0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5}}, // IDE
+
+};
+
+// Channel message dispatch table
+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},
+};
+
+/*++
+
+Name:
+       AllocVmbusChannel()
+
+Description:
+       Allocate and initialize a vmbus channel object
+
+--*/
+VMBUS_CHANNEL* AllocVmbusChannel(void)
+{
+       VMBUS_CHANNEL* channel;
+
+       channel = (VMBUS_CHANNEL*) MemAllocAtomic(sizeof(VMBUS_CHANNEL));
+       if (!channel)
+       {
+               return NULL;
+       }
+
+       memset(channel, 0,sizeof(VMBUS_CHANNEL));
+       channel->InboundLock = SpinlockCreate();
+       if (!channel->InboundLock)
+       {
+               MemFree(channel);
+               return NULL;
+       }
+
+       channel->PollTimer = TimerCreate(VmbusChannelOnTimer, channel);
+       if (!channel->PollTimer)
+       {
+               SpinlockClose(channel->InboundLock);
+               MemFree(channel);
+               return NULL;
+       }
+
+       //channel->dataWorkQueue = WorkQueueCreate("data");
+       channel->ControlWQ = WorkQueueCreate("control");
+       if (!channel->ControlWQ)
+       {
+               TimerClose(channel->PollTimer);
+               SpinlockClose(channel->InboundLock);
+               MemFree(channel);
+               return NULL;
+       }
+
+       return channel;
+}
+
+/*++
+
+Name:
+       ReleaseVmbusChannel()
+
+Description:
+       Release the vmbus channel object itself
+
+--*/
+static inline void ReleaseVmbusChannel(void* Context)
+{
+       VMBUS_CHANNEL* channel = (VMBUS_CHANNEL*)Context;
+
+       DPRINT_ENTER(VMBUS);
+
+       DPRINT_DBG(VMBUS, "releasing channel (%p)", channel);
+       WorkQueueClose(channel->ControlWQ);
+       DPRINT_DBG(VMBUS, "channel released (%p)", channel);
+
+       MemFree(channel);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+/*++
+
+Name:
+       FreeVmbusChannel()
+
+Description:
+       Release the resources used by the vmbus channel object
+
+--*/
+void FreeVmbusChannel(VMBUS_CHANNEL* Channel)
+{
+       SpinlockClose(Channel->InboundLock);
+       TimerClose(Channel->PollTimer);
+
+       // We have to release the channel's workqueue/thread in the vmbus's workqueue/thread context
+       // ie we can't destroy ourselves.
+       WorkQueueQueueWorkItem(gVmbusConnection.WorkQueue, ReleaseVmbusChannel, (void*)Channel);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelProcessOffer()
+
+Description:
+       Process the offer by creating a channel/device associated with this offer
+
+--*/
+static void
+VmbusChannelProcessOffer(
+       PVOID context
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL* newChannel=(VMBUS_CHANNEL*)context;
+       LIST_ENTRY* anchor;
+       LIST_ENTRY* curr;
+       BOOL fNew=TRUE;
+       VMBUS_CHANNEL* channel;
+
+       DPRINT_ENTER(VMBUS);
+
+       // Make sure this is a new offer
+       SpinlockAcquire(gVmbusConnection.ChannelLock);
+
+       ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList)
+       {
+               channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry);
+
+               if (!memcmp(&channel->OfferMsg.Offer.InterfaceType, &newChannel->OfferMsg.Offer.InterfaceType,sizeof(GUID)) &&
+                       !memcmp(&channel->OfferMsg.Offer.InterfaceInstance, &newChannel->OfferMsg.Offer.InterfaceInstance, sizeof(GUID)))
+               {
+                       fNew = FALSE;
+                       break;
+               }
+       }
+
+       if (fNew)
+       {
+               INSERT_TAIL_LIST(&gVmbusConnection.ChannelList, &newChannel->ListEntry);
+       }
+       SpinlockRelease(gVmbusConnection.ChannelLock);
+
+       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);
+
+               SpinlockAcquire(gVmbusConnection.ChannelLock);
+               REMOVE_ENTRY_LIST(&newChannel->ListEntry);
+               SpinlockRelease(gVmbusConnection.ChannelLock);
+
+               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);
+}
+
+/*++
+
+Name:
+       VmbusChannelProcessRescindOffer()
+
+Description:
+       Rescind the offer by initiating a device removal
+
+--*/
+static void
+VmbusChannelProcessRescindOffer(
+       PVOID context
+       )
+{
+       VMBUS_CHANNEL* channel=(VMBUS_CHANNEL*)context;
+
+       DPRINT_ENTER(VMBUS);
+
+       VmbusChildDeviceRemove(channel->DeviceObject);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnOffer()
+
+Description:
+       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(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       )
+{
+       VMBUS_CHANNEL_OFFER_CHANNEL* offer = (VMBUS_CHANNEL_OFFER_CHANNEL*)hdr;
+       VMBUS_CHANNEL* newChannel;
+
+       GUID *guidType;
+       GUID *guidInstance;
+       int i;
+       int fSupported=0;
+
+       DPRINT_ENTER(VMBUS);
+
+       for (i=0; i<MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++)
+       {
+               if (memcmp(&offer->Offer.InterfaceType, &gSupportedDeviceClasses[i], sizeof(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(VMBUS_CHANNEL_OFFER_CHANNEL));
+       newChannel->MonitorGroup = (UINT8)offer->MonitorId / 32;
+       newChannel->MonitorBit = (UINT8)offer->MonitorId % 32;
+
+       // TODO: Make sure the offer comes from our parent partition
+       WorkQueueQueueWorkItem(newChannel->ControlWQ, VmbusChannelProcessOffer, newChannel);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnOfferRescind()
+
+Description:
+       Rescind offer handler. We queue a work item to process this offer
+       synchronously
+
+--*/
+static void
+VmbusChannelOnOfferRescind(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       )
+{
+       VMBUS_CHANNEL_RESCIND_OFFER* rescind = (VMBUS_CHANNEL_RESCIND_OFFER*)hdr;
+       VMBUS_CHANNEL* channel;
+
+       DPRINT_ENTER(VMBUS);
+
+       channel = GetChannelFromRelId(rescind->ChildRelId);
+       if (channel == NULL)
+       {
+               DPRINT_DBG(VMBUS, "channel not found for relId %d", rescind->ChildRelId);
+               return;
+       }
+
+       WorkQueueQueueWorkItem(channel->ControlWQ, VmbusChannelProcessRescindOffer, channel);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnOffersDelivered()
+
+Description:
+       This is invoked when all offers have been delivered.
+       Nothing to do here.
+
+--*/
+static void
+VmbusChannelOnOffersDelivered(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       )
+{
+       DPRINT_ENTER(VMBUS);
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnOpenResult()
+
+Description:
+       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(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       )
+{
+       VMBUS_CHANNEL_OPEN_RESULT* result = (VMBUS_CHANNEL_OPEN_RESULT*)hdr;
+       LIST_ENTRY* anchor;
+       LIST_ENTRY* curr;
+       VMBUS_CHANNEL_MSGINFO* msgInfo;
+       VMBUS_CHANNEL_MESSAGE_HEADER* requestHeader;
+       VMBUS_CHANNEL_OPEN_CHANNEL* openMsg;
+
+       DPRINT_ENTER(VMBUS);
+
+       DPRINT_DBG(VMBUS, "vmbus open result - %d", result->Status);
+
+       // Find the open msg, copy the result and signal/unblock the wait event
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+
+       ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
+       {
+               msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
+               requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
+
+               if (requestHeader->MessageType == ChannelMessageOpenChannel)
+               {
+                       openMsg = (VMBUS_CHANNEL_OPEN_CHANNEL*)msgInfo->Msg;
+                       if (openMsg->ChildRelId == result->ChildRelId &&
+                               openMsg->OpenId == result->OpenId)
+                       {
+                               memcpy(&msgInfo->Response.OpenResult, result, sizeof(VMBUS_CHANNEL_OPEN_RESULT));
+                               WaitEventSet(msgInfo->WaitEvent);
+                               break;
+                       }
+               }
+       }
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnGpadlCreated()
+
+Description:
+       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(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       )
+{
+       VMBUS_CHANNEL_GPADL_CREATED *gpadlCreated = (VMBUS_CHANNEL_GPADL_CREATED*)hdr;
+       LIST_ENTRY *anchor;
+       LIST_ENTRY *curr;
+       VMBUS_CHANNEL_MSGINFO *msgInfo;
+       VMBUS_CHANNEL_MESSAGE_HEADER *requestHeader;
+       VMBUS_CHANNEL_GPADL_HEADER *gpadlHeader;
+
+       DPRINT_ENTER(VMBUS);
+
+       DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d", gpadlCreated->CreationStatus);
+
+       // Find the establish msg, copy the result and signal/unblock the wait event
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+
+       ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
+       {
+               msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
+               requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
+
+               if (requestHeader->MessageType == ChannelMessageGpadlHeader)
+               {
+                       gpadlHeader = (VMBUS_CHANNEL_GPADL_HEADER*)requestHeader;
+
+                       if ((gpadlCreated->ChildRelId == gpadlHeader->ChildRelId) &&
+                                       (gpadlCreated->Gpadl == gpadlHeader->Gpadl))
+                       {
+                               memcpy(&msgInfo->Response.GpadlCreated, gpadlCreated, sizeof(VMBUS_CHANNEL_GPADL_CREATED));
+                               WaitEventSet(msgInfo->WaitEvent);
+                               break;
+                       }
+               }
+       }
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnGpadlTorndown()
+
+Description:
+       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(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       )
+{
+       VMBUS_CHANNEL_GPADL_TORNDOWN* gpadlTorndown  = (VMBUS_CHANNEL_GPADL_TORNDOWN*)hdr;
+       LIST_ENTRY* anchor;
+       LIST_ENTRY* curr;
+       VMBUS_CHANNEL_MSGINFO* msgInfo;
+       VMBUS_CHANNEL_MESSAGE_HEADER *requestHeader;
+       VMBUS_CHANNEL_GPADL_TEARDOWN *gpadlTeardown;
+
+       DPRINT_ENTER(VMBUS);
+
+       // Find the open msg, copy the result and signal/unblock the wait event
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+
+       ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
+       {
+               msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
+               requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
+
+               if (requestHeader->MessageType == ChannelMessageGpadlTeardown)
+               {
+                       gpadlTeardown = (VMBUS_CHANNEL_GPADL_TEARDOWN*)requestHeader;
+
+                       if (gpadlTorndown->Gpadl == gpadlTeardown->Gpadl)
+                       {
+                               memcpy(&msgInfo->Response.GpadlTorndown, gpadlTorndown, sizeof(VMBUS_CHANNEL_GPADL_TORNDOWN));
+                               WaitEventSet(msgInfo->WaitEvent);
+                               break;
+                       }
+               }
+       }
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelOnVersionResponse()
+
+Description:
+       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(
+       PVMBUS_CHANNEL_MESSAGE_HEADER hdr
+       )
+{
+       LIST_ENTRY* anchor;
+       LIST_ENTRY* curr;
+       VMBUS_CHANNEL_MSGINFO *msgInfo;
+       VMBUS_CHANNEL_MESSAGE_HEADER *requestHeader;
+       VMBUS_CHANNEL_INITIATE_CONTACT *initiate;
+       VMBUS_CHANNEL_VERSION_RESPONSE *versionResponse  = (VMBUS_CHANNEL_VERSION_RESPONSE*)hdr;
+
+       DPRINT_ENTER(VMBUS);
+
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+
+       ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
+       {
+               msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
+               requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
+
+               if (requestHeader->MessageType == ChannelMessageInitiateContact)
+               {
+                       initiate = (VMBUS_CHANNEL_INITIATE_CONTACT*)requestHeader;
+                       memcpy(&msgInfo->Response.VersionResponse, versionResponse, sizeof(VMBUS_CHANNEL_VERSION_RESPONSE));
+                       WaitEventSet(msgInfo->WaitEvent);
+               }
+       }
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusOnChannelMessage()
+
+Description:
+       Handler for channel protocol messages.
+       This is invoked in the vmbus worker thread context.
+
+--*/
+VOID
+VmbusOnChannelMessage(
+       void *Context
+       )
+{
+       HV_MESSAGE *msg=(HV_MESSAGE*)Context;
+       VMBUS_CHANNEL_MESSAGE_HEADER* hdr;
+       int size;
+
+       DPRINT_ENTER(VMBUS);
+
+       hdr = (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);
+               PrintBytes((unsigned char *)msg->u.Payload, size);
+               MemFree(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()
+       MemFree(msg);
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusChannelRequestOffers()
+
+Description:
+       Send a request to get all our pending offers.
+
+--*/
+int
+VmbusChannelRequestOffers(
+       VOID
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL_MESSAGE_HEADER* msg;
+       VMBUS_CHANNEL_MSGINFO* msgInfo;
+
+       DPRINT_ENTER(VMBUS);
+
+       msgInfo =
+               (VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_MESSAGE_HEADER));
+       ASSERT(msgInfo != NULL);
+
+       msgInfo->WaitEvent = WaitEventCreate();
+       msg = (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(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;
+       }
+       //WaitEventWait(msgInfo->waitEvent);
+
+       /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
+       REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
+       SpinlockRelease(gVmbusConnection.channelMsgLock);*/
+
+
+Cleanup:
+       if (msgInfo)
+       {
+               WaitEventClose(msgInfo->WaitEvent);
+               MemFree(msgInfo);
+       }
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+/*++
+
+Name:
+       VmbusChannelReleaseUnattachedChannels()
+
+Description:
+       Release channels that are unattached/unconnected ie (no drivers associated)
+
+--*/
+void
+VmbusChannelReleaseUnattachedChannels(
+       VOID
+       )
+{
+       LIST_ENTRY *entry;
+       VMBUS_CHANNEL *channel;
+       VMBUS_CHANNEL *start=NULL;
+
+       SpinlockAcquire(gVmbusConnection.ChannelLock);
+
+       while (!IsListEmpty(&gVmbusConnection.ChannelList))
+       {
+               entry = TOP_LIST_ENTRY(&gVmbusConnection.ChannelList);
+               channel = CONTAINING_RECORD(entry, VMBUS_CHANNEL, ListEntry);
+
+               if (channel == start)
+                       break;
+
+               if (!channel->DeviceObject->Driver)
+               {
+                       REMOVE_ENTRY_LIST(&channel->ListEntry);
+                       DPRINT_INFO(VMBUS, "Releasing unattached device object %p", channel->DeviceObject);
+
+                       VmbusChildDeviceRemove(channel->DeviceObject);
+                       FreeVmbusChannel(channel);
+               }
+               else
+               {
+                       if (!start)
+                       {
+                               start = channel;
+                       }
+               }
+       }
+
+       SpinlockRelease(gVmbusConnection.ChannelLock);
+}
+
+// eof
+
diff --git a/drivers/staging/hv/ChannelMgmt.h b/drivers/staging/hv/ChannelMgmt.h
new file mode 100644 (file)
index 0000000..d5ba5d1
--- /dev/null
@@ -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>
+ *
+ */
+
+
+#ifndef _CHANNEL_MGMT_H_
+#define _CHANNEL_MGMT_H_
+
+#include "osd.h"
+#include "List.h"
+#include "RingBuffer.h"
+
+#include "VmbusChannelInterface.h"
+#include "ChannelMessages.h"
+
+
+
+typedef void (*PFN_CHANNEL_CALLBACK)(PVOID context);
+
+typedef enum {
+       CHANNEL_OFFER_STATE,
+       CHANNEL_OPENING_STATE,
+       CHANNEL_OPEN_STATE,
+} VMBUS_CHANNEL_STATE;
+
+typedef struct _VMBUS_CHANNEL {
+       LIST_ENTRY                                      ListEntry;
+
+       DEVICE_OBJECT*                          DeviceObject;
+
+       HANDLE                                          PollTimer; // SA-111 workaround
+
+       VMBUS_CHANNEL_STATE                     State;
+
+       VMBUS_CHANNEL_OFFER_CHANNEL OfferMsg;
+       // These are based on the OfferMsg.MonitorId. Save it here for easy access.
+       UINT8                                           MonitorGroup;
+       UINT8                                           MonitorBit;
+
+       UINT32                                          RingBufferGpadlHandle;
+
+       // Allocated memory for ring buffer
+       VOID*                                           RingBufferPages;
+       UINT32                                          RingBufferPageCount;
+       RING_BUFFER_INFO                        Outbound;       // send to parent
+       RING_BUFFER_INFO                        Inbound;        // receive from parent
+       HANDLE                                          InboundLock;
+       HANDLE                                          ControlWQ;
+
+       // Channel callback are invoked in this workqueue context
+       //HANDLE                                                dataWorkQueue;
+
+       PFN_CHANNEL_CALLBACK            OnChannelCallback;
+       PVOID                                           ChannelCallbackContext;
+
+} VMBUS_CHANNEL;
+
+
+typedef struct _VMBUS_CHANNEL_DEBUG_INFO {
+       UINT32                                          RelId;
+       VMBUS_CHANNEL_STATE                     State;
+       GUID                                            InterfaceType;
+    GUID                                               InterfaceInstance;
+       UINT32                                          MonitorId;
+       UINT32                                          ServerMonitorPending;
+       UINT32                                          ServerMonitorLatency;
+       UINT32                                          ServerMonitorConnectionId;
+       UINT32                                          ClientMonitorPending;
+       UINT32                                          ClientMonitorLatency;
+       UINT32                                          ClientMonitorConnectionId;
+
+       RING_BUFFER_DEBUG_INFO          Inbound;
+       RING_BUFFER_DEBUG_INFO          Outbound;
+} VMBUS_CHANNEL_DEBUG_INFO;
+
+
+typedef union {
+       VMBUS_CHANNEL_VERSION_SUPPORTED         VersionSupported;
+       VMBUS_CHANNEL_OPEN_RESULT                       OpenResult;
+       VMBUS_CHANNEL_GPADL_TORNDOWN            GpadlTorndown;
+       VMBUS_CHANNEL_GPADL_CREATED                     GpadlCreated;
+       VMBUS_CHANNEL_VERSION_RESPONSE          VersionResponse;
+} VMBUS_CHANNEL_MESSAGE_RESPONSE;
+
+
+// Represents each channel msg on the vmbus connection
+// This is a variable-size data structure depending on
+// the msg type itself
+typedef struct _VMBUS_CHANNEL_MSGINFO {
+       // Bookkeeping stuff
+       LIST_ENTRY              MsgListEntry;
+
+       // So far, this is only used to handle gpadl body message
+       LIST_ENTRY              SubMsgList;
+
+       // Synchronize the request/response if needed
+       HANDLE                  WaitEvent;
+
+       VMBUS_CHANNEL_MESSAGE_RESPONSE Response;
+
+       UINT32                  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];
+} VMBUS_CHANNEL_MSGINFO;
+
+
+//
+// Routines
+//
+
+INTERNAL VMBUS_CHANNEL*
+AllocVmbusChannel(
+       void
+       );
+
+INTERNAL void
+FreeVmbusChannel(
+       VMBUS_CHANNEL *Channel
+       );
+
+INTERNAL void
+VmbusOnChannelMessage(
+       void *Context
+       );
+
+INTERNAL int
+VmbusChannelRequestOffers(
+       void
+       );
+
+INTERNAL 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 (file)
index 0000000..fba195a
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ *
+ * 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 "logging.h"
+
+#include "VmbusPrivate.h"
+
+//
+// Globals
+//
+
+
+VMBUS_CONNECTION gVmbusConnection = {
+       .ConnectState           = Disconnected,
+       .NextGpadlHandle        = 0xE1E10,
+};
+
+
+/*++
+
+Name:
+       VmbusConnect()
+
+Description:
+       Sends a connect request on the partition service connection
+
+--*/
+int
+VmbusConnect(
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL_MSGINFO *msgInfo=NULL;
+       VMBUS_CHANNEL_INITIATE_CONTACT *msg;
+
+       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 = WorkQueueCreate("vmbusQ");
+
+       INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelMsgList);
+       gVmbusConnection.ChannelMsgLock = SpinlockCreate();
+
+       INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelList);
+       gVmbusConnection.ChannelLock = SpinlockCreate();
+
+       // Setup the vmbus event connection for channel interrupt abstraction stuff
+       gVmbusConnection.InterruptPage = PageAlloc(1);
+       if (gVmbusConnection.InterruptPage == NULL)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage;
+       gVmbusConnection.SendInterruptPage = (void*)((ULONG_PTR)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 = PageAlloc(2);
+       if (gVmbusConnection.MonitorPages == NULL)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       msgInfo = (VMBUS_CHANNEL_MSGINFO*)MemAllocZeroed(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
+       if (msgInfo == NULL)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       msgInfo->WaitEvent = WaitEventCreate();
+       msg = (VMBUS_CHANNEL_INITIATE_CONTACT*)msgInfo->Msg;
+
+       msg->Header.MessageType = ChannelMessageInitiateContact;
+       msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER;
+       msg->InterruptPage = GetPhysicalAddress(gVmbusConnection.InterruptPage);
+       msg->MonitorPage1 = GetPhysicalAddress(gVmbusConnection.MonitorPages);
+       msg->MonitorPage2 = GetPhysicalAddress((PVOID)((ULONG_PTR)gVmbusConnection.MonitorPages + PAGE_SIZE));
+
+       // Add to list before we send the request since we may receive the response
+       // before returning from this routine
+       SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
+       INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry);
+       SpinlockRelease(gVmbusConnection.ChannelMsgLock);
+
+       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(VMBUS_CHANNEL_INITIATE_CONTACT));
+       if (ret != 0)
+       {
+               REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
+               goto Cleanup;
+       }
+
+       // Wait for the connection response
+       WaitEventWait(msgInfo->WaitEvent);
+
+       REMOVE_ENTRY_LIST(&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;
+       }
+
+
+       WaitEventClose(msgInfo->WaitEvent);
+       MemFree(msgInfo);
+       DPRINT_EXIT(VMBUS);
+
+       return 0;
+
+Cleanup:
+
+       gVmbusConnection.ConnectState = Disconnected;
+
+       WorkQueueClose(gVmbusConnection.WorkQueue);
+       SpinlockClose(gVmbusConnection.ChannelLock);
+       SpinlockClose(gVmbusConnection.ChannelMsgLock);
+
+       if (gVmbusConnection.InterruptPage)
+       {
+               PageFree(gVmbusConnection.InterruptPage, 1);
+               gVmbusConnection.InterruptPage = NULL;
+       }
+
+       if (gVmbusConnection.MonitorPages)
+       {
+               PageFree(gVmbusConnection.MonitorPages, 2);
+               gVmbusConnection.MonitorPages = NULL;
+       }
+
+       if (msgInfo)
+       {
+               if (msgInfo->WaitEvent)
+                       WaitEventClose(msgInfo->WaitEvent);
+
+               MemFree(msgInfo);
+       }
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+
+/*++
+
+Name:
+       VmbusDisconnect()
+
+Description:
+       Sends a disconnect request on the partition service connection
+
+--*/
+int
+VmbusDisconnect(
+       VOID
+       )
+{
+       int ret=0;
+       VMBUS_CHANNEL_UNLOAD *msg;
+
+       DPRINT_ENTER(VMBUS);
+
+       // Make sure we are connected
+       if (gVmbusConnection.ConnectState != Connected)
+               return -1;
+
+       msg = MemAllocZeroed(sizeof(VMBUS_CHANNEL_UNLOAD));
+
+       msg->MessageType = ChannelMessageUnload;
+
+       ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_UNLOAD));
+
+       if (ret != 0)
+       {
+               goto Cleanup;
+       }
+
+       PageFree(gVmbusConnection.InterruptPage, 1);
+
+       // TODO: iterate thru the msg list and free up
+
+       SpinlockClose(gVmbusConnection.ChannelMsgLock);
+
+       WorkQueueClose(gVmbusConnection.WorkQueue);
+
+       gVmbusConnection.ConnectState = Disconnected;
+
+       DPRINT_INFO(VMBUS, "Vmbus disconnected!!");
+
+Cleanup:
+       if (msg)
+       {
+               MemFree(msg);
+       }
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+
+/*++
+
+Name:
+       GetChannelFromRelId()
+
+Description:
+       Get the channel object given its child relative id (ie channel id)
+
+--*/
+VMBUS_CHANNEL*
+GetChannelFromRelId(
+       UINT32 relId
+       )
+{
+       VMBUS_CHANNEL* channel;
+       VMBUS_CHANNEL* foundChannel=NULL;
+       LIST_ENTRY* anchor;
+       LIST_ENTRY* curr;
+
+       SpinlockAcquire(gVmbusConnection.ChannelLock);
+       ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList)
+       {
+               channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry);
+
+               if (channel->OfferMsg.ChildRelId == relId)
+               {
+                       foundChannel = channel;
+                       break;
+               }
+       }
+       SpinlockRelease(gVmbusConnection.ChannelLock);
+
+       return foundChannel;
+}
+
+
+
+/*++
+
+Name:
+       VmbusProcessChannelEvent()
+
+Description:
+       Process a channel event notification
+
+--*/
+static void
+VmbusProcessChannelEvent(
+       PVOID context
+       )
+{
+       VMBUS_CHANNEL* channel;
+       UINT32 relId = (UINT32)(ULONG_PTR)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);
+       }
+}
+
+
+/*++
+
+Name:
+       VmbusOnEvents()
+
+Description:
+       Handler for events
+
+--*/
+VOID
+VmbusOnEvents(
+  VOID
+       )
+{
+       int dword;
+       //int maxdword = PAGE_SIZE >> 3; // receive size is 1/2 page and divide that by 4 bytes
+       int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
+       int bit;
+       int relid;
+       UINT32* recvInterruptPage = gVmbusConnection.RecvInterruptPage;
+       //VMBUS_CHANNEL_MESSAGE* receiveMsg;
+
+       DPRINT_ENTER(VMBUS);
+
+       // Check events
+       if (recvInterruptPage)
+       {
+               for (dword = 0; dword < maxdword; dword++)
+               {
+                       if (recvInterruptPage[dword])
+                       {
+                               for (bit = 0; bit < 32; bit++)
+                               {
+                                       if (BitTestAndClear(&recvInterruptPage[dword], bit))
+                                       {
+                                               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*)(ULONG_PTR)relid);
+                                               }
+                                       }
+                               }
+                       }
+                }
+       }
+       DPRINT_EXIT(VMBUS);
+
+       return;
+}
+
+/*++
+
+Name:
+       VmbusPostMessage()
+
+Description:
+       Send a msg on the vmbus's message connection
+
+--*/
+int
+VmbusPostMessage(
+       PVOID                   buffer,
+       SIZE_T                  bufferLen
+       )
+{
+       int ret=0;
+       HV_CONNECTION_ID connId;
+
+
+       connId.AsUINT32 =0;
+       connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID;
+       ret = HvPostMessage(
+                       connId,
+                       1,
+                       buffer,
+                       bufferLen);
+
+       return  ret;
+}
+
+/*++
+
+Name:
+       VmbusSetEvent()
+
+Description:
+       Send an event notification to the parent
+
+--*/
+int
+VmbusSetEvent(UINT32 childRelId)
+{
+       int ret=0;
+
+       DPRINT_ENTER(VMBUS);
+
+       // Each UINT32 represents 32 channels
+       BitSet((UINT32*)gVmbusConnection.SendInterruptPage + (childRelId >> 5), childRelId & 31);
+       ret = HvSignalEvent();
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+// EOF
diff --git a/drivers/staging/hv/Hv.c b/drivers/staging/hv/Hv.c
new file mode 100644 (file)
index 0000000..7aec8c9
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ *
+ * 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 "logging.h"
+#include "VmbusPrivate.h"
+
+//
+// Globals
+//
+
+// The one and only
+HV_CONTEXT gHvContext={
+       .SynICInitialized = FALSE,
+       .HypercallPage = NULL,
+       .SignalEventParam = NULL,
+       .SignalEventBuffer = NULL,
+};
+
+
+/*++
+
+Name:
+       HvQueryHypervisorPresence()
+
+Description:
+       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;
+    do_cpuid(op, &eax, &ebx, &ecx, &edx);
+
+       return (ecx & HV_PRESENT_BIT);
+}
+
+
+/*++
+
+Name:
+       HvQueryHypervisorInfo()
+
+Description:
+       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;
+    do_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;
+    do_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;
+        do_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;
+}
+
+
+/*++
+
+Name:
+       HvDoHypercall()
+
+Description:
+       Invoke the specified hypercall
+
+--*/
+static UINT64
+HvDoHypercall (
+    UINT64  Control,
+    void*   Input,
+    void*   Output
+    )
+{
+#ifdef x86_64
+    UINT64 hvStatus=0;
+    UINT64 inputAddress = (Input)? GetPhysicalAddress(Input) : 0;
+       UINT64 outputAddress = (Output)? GetPhysicalAddress(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
+
+    UINT32 controlHi = Control >> 32;
+    UINT32 controlLo = Control & 0xFFFFFFFF;
+    UINT32 hvStatusHi = 1;
+    UINT32 hvStatusLo = 1;
+    UINT64 inputAddress = (Input) ? GetPhysicalAddress(Input) : 0;
+    UINT32 inputAddressHi = inputAddress >> 32;
+    UINT32 inputAddressLo = inputAddress & 0xFFFFFFFF;
+       UINT64 outputAddress = (Output) ?GetPhysicalAddress(Output) : 0;
+    UINT32 outputAddressHi = outputAddress >> 32;
+    UINT32 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 | ((UINT64)hvStatusHi << 32));
+
+    return (hvStatusLo | ((UINT64)hvStatusHi << 32));
+#endif // x86_64
+}
+
+/*++
+
+Name:
+       HvInit()
+
+Description:
+       Main initialization routine. This routine must be called
+       before any other routines in here are called
+
+--*/
+static int
+HvInit (
+    void
+    )
+{
+       int ret=0;
+    int maxLeaf;
+       HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
+       void* virtAddr=0;
+       ULONG_PTR physAddr=0;
+
+       DPRINT_ENTER(VMBUS);
+
+       memset(gHvContext.synICEventPage, 0, sizeof(HANDLE)*MAX_NUM_CPUS);
+       memset(gHvContext.synICMessagePage, 0, sizeof(HANDLE)*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
+       gHvContext.GuestId = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
+
+       if (gHvContext.GuestId == 0)
+       {
+               // Write our OS info
+               WriteMsr(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
+
+               gHvContext.GuestId = HV_LINUX_GUEST_ID;
+       }
+
+       // See if the hypercall page is already set
+       hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
+
+       if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
+       {
+               // Allocate the hypercall page memory
+               //virtAddr = PageAlloc(1);
+               virtAddr = VirtualAllocExec(PAGE_SIZE);
+
+               if (!virtAddr)
+               {
+                       DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
+                       goto Cleanup;
+               }
+
+               hypercallMsr.Enable = 1;
+               //hypercallMsr.GuestPhysicalAddress = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT;
+               hypercallMsr.GuestPhysicalAddress = Virtual2Physical(virtAddr) >> PAGE_SHIFT;
+               WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+
+        // Confirm that hypercall page did get setup.
+               hypercallMsr.AsUINT64 = 0;
+               hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
+
+               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=0x%08x, PA=0x%08x",
+               (unsigned long)gHvContext.HypercallPage,
+               (unsigned long)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
+
+       // Setup the global signal event param for the signal event hypercall
+       gHvContext.SignalEventBuffer = MemAlloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER));
+       if (!gHvContext.SignalEventBuffer)
+       {
+               goto Cleanup;
+       }
+
+       gHvContext.SignalEventParam = (PHV_INPUT_SIGNAL_EVENT)(ALIGN_UP((ULONG_PTR)gHvContext.SignalEventBuffer, HV_HYPERCALL_PARAM_ALIGN));
+       gHvContext.SignalEventParam->ConnectionId.AsUINT32 = 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;
+                       WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+               }
+
+               VirtualFree(virtAddr);
+       }
+       ret = -1;
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+
+/*++
+
+Name:
+       HvCleanup()
+
+Description:
+       Cleanup routine. This routine is called normally during driver unloading or exiting.
+
+--*/
+void
+HvCleanup (
+    void
+    )
+{
+       HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
+
+       DPRINT_ENTER(VMBUS);
+
+       if (gHvContext.SignalEventBuffer)
+       {
+               MemFree(gHvContext.SignalEventBuffer);
+               gHvContext.SignalEventBuffer = NULL;
+               gHvContext.SignalEventParam = NULL;
+       }
+
+       if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
+       {
+               if (gHvContext.HypercallPage)
+               {
+                       hypercallMsr.AsUINT64 = 0;
+                       WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
+                       VirtualFree(gHvContext.HypercallPage);
+                       gHvContext.HypercallPage = NULL;
+               }
+       }
+
+       DPRINT_EXIT(VMBUS);
+
+}
+
+
+/*++
+
+Name:
+       HvPostMessage()
+
+Description:
+       Post a message using the hypervisor message IPC. This
+       involves a hypercall.
+
+--*/
+HV_STATUS
+HvPostMessage(
+       HV_CONNECTION_ID connectionId,
+       HV_MESSAGE_TYPE  messageType,
+       PVOID            payload,
+       SIZE_T           payloadSize
+       )
+{
+       struct alignedInput {
+               UINT64                                  alignment8;
+               HV_INPUT_POST_MESSAGE   msg;
+       };
+
+       PHV_INPUT_POST_MESSAGE alignedMsg;
+       HV_STATUS status;
+       ULONG_PTR addr;
+
+       if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
+       {
+               return -1;
+       }
+
+       addr = (ULONG_PTR)MemAllocAtomic(sizeof(struct alignedInput));
+
+       if (!addr)
+       {
+               return -1;
+       }
+
+       alignedMsg = (PHV_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, 0) & 0xFFFF;
+
+       MemFree((void*)addr);
+
+       return status;
+}
+
+
+/*++
+
+Name:
+       HvSignalEvent()
+
+Description:
+       Signal an event on the specified connection using the hypervisor event IPC. This
+       involves a hypercall.
+
+--*/
+HV_STATUS
+HvSignalEvent(
+       )
+{
+       HV_STATUS status;
+
+       status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, 0) & 0xFFFF;
+
+       return status;
+}
+
+
+/*++
+
+Name:
+       HvSynicInit()
+
+Description:
+       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 (
+       UINT32 irqVector
+       )
+{
+       UINT64                  version;
+       HV_SYNIC_SIMP   simp;
+       HV_SYNIC_SIEFP  siefp;
+    HV_SYNIC_SINT      sharedSint;
+       HV_SYNIC_SCONTROL sctrl;
+       UINT64                  guestID;
+       int ret=0;
+
+       DPRINT_ENTER(VMBUS);
+
+       if (!gHvContext.HypercallPage)
+       {
+               DPRINT_EXIT(VMBUS);
+               return ret;
+       }
+
+       // Check the version
+       version = ReadMsr(HV_X64_MSR_SVERSION);
+
+       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.");
+
+               simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
+               siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
+
+               DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
+
+               // Determine if we are running on xenlinux (ie x2v shim) or native linux
+               guestID = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
+
+               if (guestID == HV_LINUX_GUEST_ID)
+               {
+                       gHvContext.synICMessagePage[0] = GetVirtualAddress(simp.BaseSimpGpa << PAGE_SHIFT);
+                       gHvContext.synICEventPage[0] = GetVirtualAddress(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] = PageAlloc(1);
+               if (gHvContext.synICMessagePage[0] == NULL)
+               {
+                       DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
+                       goto Cleanup;
+               }
+
+               gHvContext.synICEventPage[0] = PageAlloc(1);
+               if (gHvContext.synICEventPage[0] == NULL)
+               {
+                       DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
+                       goto Cleanup;
+               }
+
+               //
+               // Setup the Synic's message page
+               //
+               simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
+               simp.SimpEnabled = 1;
+               simp.BaseSimpGpa = GetPhysicalAddress(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
+
+               DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
+
+               WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
+
+               //
+               // Setup the Synic's event page
+               //
+               siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
+               siefp.SiefpEnabled = 1;
+               siefp.BaseSiefpGpa = GetPhysicalAddress(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
+
+               DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
+
+               WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
+       }
+       //
+    // Setup the interception SINT.
+    //
+       //WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX),
+    //             interceptionSint.AsUINT64);
+
+    //
+    // Setup the shared SINT.
+    //
+       sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
+
+       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);
+
+       WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
+
+       // Enable the global synic bit
+       sctrl.AsUINT64 = ReadMsr(HV_X64_MSR_SCONTROL);
+       sctrl.Enable = 1;
+
+    WriteMsr(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])
+               {
+                       PageFree(gHvContext.synICEventPage[0],1);
+               }
+
+               if (gHvContext.synICMessagePage[0])
+               {
+                       PageFree(gHvContext.synICMessagePage[0], 1);
+               }
+       }
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+
+}
+
+/*++
+
+Name:
+       HvSynicCleanup()
+
+Description:
+       Cleanup routine for HvSynicInit().
+
+--*/
+VOID
+HvSynicCleanup(
+       VOID
+       )
+{
+    HV_SYNIC_SINT      sharedSint;
+       HV_SYNIC_SIMP   simp;
+       HV_SYNIC_SIEFP  siefp;
+
+       DPRINT_ENTER(VMBUS);
+
+       if (!gHvContext.SynICInitialized)
+       {
+               DPRINT_EXIT(VMBUS);
+               return;
+       }
+
+       sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
+
+       sharedSint.Masked = 1;
+
+       // Disable the interrupt
+    WriteMsr(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)
+       {
+               simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
+               simp.SimpEnabled = 0;
+               simp.BaseSimpGpa = 0;
+
+               WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
+
+               siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
+               siefp.SiefpEnabled = 0;
+               siefp.BaseSiefpGpa = 0;
+
+               WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
+
+               PageFree(gHvContext.synICMessagePage[0], 1);
+               PageFree(gHvContext.synICEventPage[0], 1);
+       }
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+// eof
diff --git a/drivers/staging/hv/Hv.h b/drivers/staging/hv/Hv.h
new file mode 100644 (file)
index 0000000..cbc77d2
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ *
+ * 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 "osd.h"
+
+#include "HvTypes.h"
+#include "HvStatus.h"
+//#include "HvVmApi.h"
+//#include "HvKeApi.h"
+//#include "HvMmApi.h"
+//#include "HvCpuApi.h"
+#include "HvHalApi.h"
+#include "HvVpApi.h"
+//#include "HvTrApi.h"
+#include "HvSynicApi.h"
+//#include "HvAmApi.h"
+//#include "HvHkApi.h"
+//#include "HvValApi.h"
+#include "HvHcApi.h"
+#include "HvPtApi.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           (((UINT64)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                      (((UINT64)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(UINT64)
+
+//
+// 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
+//#define VMBUS_PORT_ID                        11              // Our local vmbus's port and connection id. Anything >0 is fine
+
+// 628180B8-308D-4c5e-B7DB-1BEB62E62EF4
+static const 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
+
+
+typedef struct {
+       UINT64                                  Align8;
+       HV_INPUT_SIGNAL_EVENT   Event;
+} HV_INPUT_SIGNAL_EVENT_BUFFER;
+
+typedef struct {
+       UINT64  GuestId;                        // XenLinux or native Linux. If XenLinux, the hypercall and synic pages has already been initialized
+       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).
+       HV_INPUT_SIGNAL_EVENT_BUFFER *SignalEventBuffer;
+       HV_INPUT_SIGNAL_EVENT *SignalEventParam; // 8-bytes aligned of the buffer above
+
+       HANDLE  synICMessagePage[MAX_NUM_CPUS];
+       HANDLE  synICEventPage[MAX_NUM_CPUS];
+} HV_CONTEXT;
+
+extern HV_CONTEXT gHvContext;
+
+
+//
+// Inline routines
+//
+static inline unsigned long long ReadMsr(int msr)
+{
+       unsigned long long val;
+
+       RDMSR(msr, val);
+
+       return val;
+}
+
+static inline void WriteMsr(int msr, UINT64 val)
+{
+       WRMSR(msr, val);
+
+       return;
+}
+
+//
+// Hv Interface
+//
+INTERNAL int
+HvInit(
+    VOID
+    );
+
+INTERNAL VOID
+HvCleanup(
+    VOID
+    );
+
+INTERNAL HV_STATUS
+HvPostMessage(
+       HV_CONNECTION_ID connectionId,
+       HV_MESSAGE_TYPE  messageType,
+       PVOID            payload,
+       SIZE_T           payloadSize
+       );
+
+INTERNAL HV_STATUS
+HvSignalEvent(
+       VOID
+       );
+
+INTERNAL int
+HvSynicInit(
+       UINT32          irqVector
+       );
+
+INTERNAL VOID
+HvSynicCleanup(
+       VOID
+       );
+
+#endif // __HV_H__
diff --git a/drivers/staging/hv/RingBuffer.c b/drivers/staging/hv/RingBuffer.c
new file mode 100644 (file)
index 0000000..57d944e
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ *
+ * 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 "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, UINT32 *read, UINT32 *write)
+{
+       UINT32 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 UINT32
+GetNextWriteLocation(RING_BUFFER_INFO* RingInfo)
+{
+       UINT32 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, UINT32 NextWriteLocation)
+{
+       RingInfo->RingBuffer->WriteIndex = NextWriteLocation;
+}
+
+/*++
+
+Name:
+       GetNextReadLocation()
+
+Description:
+       Get the next read location for the specified ring buffer
+
+--*/
+static inline UINT32
+GetNextReadLocation(RING_BUFFER_INFO* RingInfo)
+{
+       UINT32 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 UINT32
+GetNextReadLocationWithOffset(RING_BUFFER_INFO* RingInfo, UINT32 Offset)
+{
+       UINT32 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, UINT32 NextReadLocation)
+{
+       RingInfo->RingBuffer->ReadIndex = NextReadLocation;
+}
+
+
+/*++
+
+Name:
+       GetRingBuffer()
+
+Description:
+       Get the start of the ring buffer
+
+--*/
+static inline PVOID
+GetRingBuffer(RING_BUFFER_INFO* RingInfo)
+{
+       return (PVOID)RingInfo->RingBuffer->Buffer;
+}
+
+
+/*++
+
+Name:
+       GetRingBufferSize()
+
+Description:
+       Get the size of the ring buffer
+
+--*/
+static inline UINT32
+GetRingBufferSize(RING_BUFFER_INFO* RingInfo)
+{
+       return RingInfo->RingDataSize;
+}
+
+/*++
+
+Name:
+       GetRingBufferIndices()
+
+Description:
+       Get the read and write indices as UINT64 of the specified ring buffer
+
+--*/
+static inline UINT64
+GetRingBufferIndices(RING_BUFFER_INFO* RingInfo)
+{
+       return ((UINT64)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)
+{
+       UINT32 bytesAvailToWrite;
+       UINT32 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 UINT32
+CopyToRingBuffer(
+       RING_BUFFER_INFO        *RingInfo,
+       UINT32                          StartWriteOffset,
+       PVOID                           Src,
+       UINT32                          SrcLen);
+
+static UINT32
+CopyFromRingBuffer(
+       RING_BUFFER_INFO        *RingInfo,
+       PVOID                           Dest,
+       UINT32                          DestLen,
+       UINT32                          StartReadOffset);
+
+
+
+/*++
+
+Name:
+       RingBufferGetDebugInfo()
+
+Description:
+       Get various debug metrics for the specified ring buffer
+
+--*/
+void
+RingBufferGetDebugInfo(
+       RING_BUFFER_INFO                *RingInfo,
+       RING_BUFFER_DEBUG_INFO  *DebugInfo
+       )
+{
+       UINT32 bytesAvailToWrite;
+       UINT32 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
+
+--*/
+UINT32
+GetRingBufferInterruptMask(
+       RING_BUFFER_INFO *rbi
+       )
+{
+       return rbi->RingBuffer->InterruptMask;
+}
+
+/*++
+
+Name:
+       RingBufferInit()
+
+Description:
+       Initialize the ring buffer
+
+--*/
+int
+RingBufferInit(
+       RING_BUFFER_INFO        *RingInfo,
+       VOID                            *Buffer,
+       UINT32                          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);
+
+       RingInfo->RingLock = SpinlockCreate();
+
+       return 0;
+}
+
+/*++
+
+Name:
+       RingBufferCleanup()
+
+Description:
+       Cleanup the ring buffer
+
+--*/
+void
+RingBufferCleanup(
+       RING_BUFFER_INFO* RingInfo
+       )
+{
+       SpinlockClose(RingInfo->RingLock);
+}
+
+/*++
+
+Name:
+       RingBufferWrite()
+
+Description:
+       Write to the ring buffer
+
+--*/
+int
+RingBufferWrite(
+       RING_BUFFER_INFO*       OutRingInfo,
+       SG_BUFFER_LIST          SgBuffers[],
+       UINT32                          SgBufferCount
+       )
+{
+       int i=0;
+       UINT32 byteAvailToWrite;
+       UINT32 byteAvailToRead;
+       UINT32 totalBytesToWrite=0;
+
+       volatile UINT32 nextWriteLocation;
+       UINT64 prevIndices=0;
+
+       DPRINT_ENTER(VMBUS);
+
+       for (i=0; i < SgBufferCount; i++)
+       {
+               totalBytesToWrite += SgBuffers[i].Length;
+       }
+
+       totalBytesToWrite += sizeof(UINT64);
+
+       SpinlockAcquire(OutRingInfo->RingLock);
+
+       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);
+
+               SpinlockRelease(OutRingInfo->RingLock);
+
+               DPRINT_EXIT(VMBUS);
+
+               return -1;
+       }
+
+       // Write to the ring buffer
+       nextWriteLocation = GetNextWriteLocation(OutRingInfo);
+
+       for (i=0; i < SgBufferCount; i++)
+       {
+                nextWriteLocation = CopyToRingBuffer(OutRingInfo,
+                                                                                               nextWriteLocation,
+                                                                                               SgBuffers[i].Data,
+                                                                                               SgBuffers[i].Length);
+       }
+
+       // Set previous packet start
+       prevIndices = GetRingBufferIndices(OutRingInfo);
+
+       nextWriteLocation = CopyToRingBuffer(OutRingInfo,
+                                                                                               nextWriteLocation,
+                                                                                               &prevIndices,
+                                                                                               sizeof(UINT64));
+
+       // Make sure we flush all writes before updating the writeIndex
+       MemoryFence();
+
+       // Now, update the write location
+       SetNextWriteLocation(OutRingInfo, nextWriteLocation);
+
+       //DumpRingInfo(OutRingInfo, "AFTER ");
+
+       SpinlockRelease(OutRingInfo->RingLock);
+
+       DPRINT_EXIT(VMBUS);
+
+       return 0;
+}
+
+
+/*++
+
+Name:
+       RingBufferPeek()
+
+Description:
+       Read without advancing the read index
+
+--*/
+int
+RingBufferPeek(
+       RING_BUFFER_INFO*       InRingInfo,
+       void*                           Buffer,
+       UINT32                          BufferLen
+       )
+{
+       UINT32 bytesAvailToWrite;
+       UINT32 bytesAvailToRead;
+       UINT32 nextReadLocation=0;
+
+       SpinlockAcquire(InRingInfo->RingLock);
+
+       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);
+
+               SpinlockRelease(InRingInfo->RingLock);
+
+               return -1;
+       }
+
+       // Convert to byte offset
+       nextReadLocation = GetNextReadLocation(InRingInfo);
+
+       nextReadLocation = CopyFromRingBuffer(InRingInfo,
+                                                                                       Buffer,
+                                                                                       BufferLen,
+                                                                                       nextReadLocation);
+
+       SpinlockRelease(InRingInfo->RingLock);
+
+       return 0;
+}
+
+
+/*++
+
+Name:
+       RingBufferRead()
+
+Description:
+       Read and advance the read index
+
+--*/
+int
+RingBufferRead(
+       RING_BUFFER_INFO*       InRingInfo,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32                          Offset
+       )
+{
+       UINT32 bytesAvailToWrite;
+       UINT32 bytesAvailToRead;
+       UINT32 nextReadLocation=0;
+       UINT64 prevIndices=0;
+
+       ASSERT(BufferLen > 0);
+
+       SpinlockAcquire(InRingInfo->RingLock);
+
+       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);
+
+               SpinlockRelease(InRingInfo->RingLock);
+
+               return -1;
+       }
+
+       nextReadLocation = GetNextReadLocationWithOffset(InRingInfo, Offset);
+
+       nextReadLocation = CopyFromRingBuffer(InRingInfo,
+                                                                                       Buffer,
+                                                                                       BufferLen,
+                                                                                       nextReadLocation);
+
+       nextReadLocation = CopyFromRingBuffer(InRingInfo,
+                                                                                       &prevIndices,
+                                                                                       sizeof(UINT64),
+                                                                                       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
+       MemoryFence();
+
+       // Update the read index
+       SetNextReadLocation(InRingInfo, nextReadLocation);
+
+       //DumpRingInfo(InRingInfo, "AFTER ");
+
+       SpinlockRelease(InRingInfo->RingLock);
+
+       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!!
+
+--*/
+UINT32
+CopyToRingBuffer(
+       RING_BUFFER_INFO        *RingInfo,
+       UINT32                          StartWriteOffset,
+       PVOID                           Src,
+       UINT32                          SrcLen)
+{
+       PVOID ringBuffer=GetRingBuffer(RingInfo);
+       UINT32 ringBufferSize=GetRingBufferSize(RingInfo);
+       UINT32 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!!
+
+--*/
+UINT32
+CopyFromRingBuffer(
+       RING_BUFFER_INFO        *RingInfo,
+       PVOID                           Dest,
+       UINT32                          DestLen,
+       UINT32                          StartReadOffset)
+{
+       PVOID ringBuffer=GetRingBuffer(RingInfo);
+       UINT32 ringBufferSize=GetRingBufferSize(RingInfo);
+
+       UINT32 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 (file)
index 0000000..9af5df0
--- /dev/null
@@ -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 _RING_BUFFER_H_
+#define _RING_BUFFER_H_
+
+#include "osd.h"
+
+typedef struct _SG_BUFFER_LIST {
+       PVOID   Data;
+       UINT32  Length;
+} SG_BUFFER_LIST;
+
+typedef struct _RING_BUFFER {
+    volatile UINT32    WriteIndex;     // Offset in bytes from the start of ring data below
+    volatile UINT32    ReadIndex;      // Offset in bytes from the start of ring data below
+
+       volatile UINT32 InterruptMask;
+       UINT8   Reserved[4084];                 // Pad it to PAGE_SIZE so that data starts on page boundary
+       // 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 UINT32 InterruptMask;
+       // Ring data starts here + RingDataStartOffset !!! DO NOT place any fields below this !!!
+    UINT8              Buffer[0];
+} STRUCT_PACKED RING_BUFFER;
+
+typedef struct _RING_BUFFER_INFO {
+    RING_BUFFER*       RingBuffer;
+    UINT32                     RingSize;                       // Include the shared header
+       HANDLE                  RingLock;
+
+    UINT32                     RingDataSize;           // < ringSize
+       UINT32                  RingDataStartOffset;
+
+} RING_BUFFER_INFO;
+
+
+typedef struct _RING_BUFFER_DEBUG_INFO {
+       UINT32          CurrentInterruptMask;
+       UINT32          CurrentReadIndex;
+       UINT32          CurrentWriteIndex;
+       UINT32          BytesAvailToRead;
+       UINT32          BytesAvailToWrite;
+}RING_BUFFER_DEBUG_INFO;
+
+
+//
+// Interface
+//
+
+INTERNAL int
+RingBufferInit(
+       RING_BUFFER_INFO        *RingInfo,
+       PVOID                           Buffer,
+       UINT32                          BufferLen
+       );
+
+INTERNAL void
+RingBufferCleanup(
+       RING_BUFFER_INFO        *RingInfo
+       );
+
+INTERNAL int
+RingBufferWrite(
+       RING_BUFFER_INFO        *RingInfo,
+       SG_BUFFER_LIST          SgBuffers[],
+       UINT32                          SgBufferCount
+       );
+
+INTERNAL int
+RingBufferPeek(
+       RING_BUFFER_INFO        *RingInfo,
+       PVOID                           Buffer,
+       UINT32                          BufferLen
+       );
+
+INTERNAL int
+RingBufferRead(
+       RING_BUFFER_INFO        *RingInfo,
+       PVOID                           Buffer,
+       UINT32                          BufferLen,
+       UINT32                          Offset
+       );
+
+INTERNAL UINT32
+GetRingBufferInterruptMask(
+       RING_BUFFER_INFO *RingInfo
+       );
+
+INTERNAL void
+DumpRingInfo(
+       RING_BUFFER_INFO* RingInfo,
+       char *Prefix
+       );
+
+INTERNAL void
+RingBufferGetDebugInfo(
+       RING_BUFFER_INFO                *RingInfo,
+       RING_BUFFER_DEBUG_INFO  *DebugInfo
+       );
+
+#endif // _RING_BUFFER_H_
diff --git a/drivers/staging/hv/Sources.c b/drivers/staging/hv/Sources.c
new file mode 100644 (file)
index 0000000..bc15464
--- /dev/null
@@ -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>
+ *
+ */
+
+
+#include "Vmbus.c"
+#include "Hv.c"
+#include "Connection.c"
+#include "Channel.c"
+#include "ChannelMgmt.c"
+#include "ChannelInterface.c"
+#include "RingBuffer.c"
diff --git a/drivers/staging/hv/VersionInfo.h b/drivers/staging/hv/VersionInfo.h
new file mode 100644 (file)
index 0000000..a827f7f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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>
+ *
+ */
+
+
+#pragma once
+
+const char VersionDate[]=__DATE__;
+const char VersionTime[]=__TIME__;
+const char VersionDesc[]= "Version 2.0";
diff --git a/drivers/staging/hv/Vmbus.c b/drivers/staging/hv/Vmbus.c
new file mode 100644 (file)
index 0000000..54a120d
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ *
+ * 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 "logging.h"
+#include "VersionInfo.h"
+#include "VmbusPrivate.h"
+
+//
+// Globals
+//
+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 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 GUID gVmbusDeviceId={
+       .Data = {0xfc, 0x60, 0x37, 0xac, 0xdf, 0x9a, 0xaa, 0x40, 0x94, 0x27, 0xa7, 0x0e, 0xd6, 0xde, 0x95, 0xc5}
+};
+
+static DRIVER_OBJECT* gDriver; // vmbus driver object
+static DEVICE_OBJECT* gDevice; // vmbus root device
+
+
+//
+// Internal routines
+//
+
+static void
+VmbusGetChannelInterface(
+       VMBUS_CHANNEL_INTERFACE *Interface
+       );
+
+static void
+VmbusGetChannelInfo(
+       DEVICE_OBJECT   *DeviceObject,
+       DEVICE_INFO             *DeviceInfo
+       );
+
+static void
+VmbusGetChannelOffers(
+       void
+       );
+
+static int
+VmbusOnDeviceAdd(
+       DEVICE_OBJECT   *Device,
+       void                    *AdditionalInfo
+       );
+
+static int
+VmbusOnDeviceRemove(
+       DEVICE_OBJECT* dev
+       );
+
+static void
+VmbusOnCleanup(
+       DRIVER_OBJECT* drv
+       );
+
+static int
+VmbusOnISR(
+       DRIVER_OBJECT* drv
+       );
+
+static void
+VmbusOnMsgDPC(
+       DRIVER_OBJECT* drv
+       );
+
+static void
+VmbusOnEventDPC(
+       DRIVER_OBJECT* drv
+       );
+
+/*++;
+
+Name:
+       VmbusInitialize()
+
+Description:
+       Main entry point
+
+--*/
+int
+VmbusInitialize(
+       DRIVER_OBJECT* drv
+       )
+{
+       VMBUS_DRIVER_OBJECT* driver = (VMBUS_DRIVER_OBJECT*)drv;
+       int ret=0;
+
+       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)=%d, sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)=%d",
+               sizeof(VMBUS_CHANNEL_PACKET_PAGE_BUFFER), sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER));
+
+       drv->name = gDriverName;
+       memcpy(&drv->deviceType, &gVmbusDeviceType, sizeof(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;
+}
+
+
+/*++;
+
+Name:
+       VmbusGetChannelOffers()
+
+Description:
+       Retrieve the channel offers from the parent partition
+
+--*/
+
+static void
+VmbusGetChannelOffers(void)
+{
+       DPRINT_ENTER(VMBUS);
+       VmbusChannelRequestOffers();
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++;
+
+Name:
+       VmbusGetChannelInterface()
+
+Description:
+       Get the channel interface
+
+--*/
+static void
+VmbusGetChannelInterface(
+       VMBUS_CHANNEL_INTERFACE *Interface
+       )
+{
+       GetChannelInterface(Interface);
+}
+
+
+/*++;
+
+Name:
+       VmbusGetChannelInterface()
+
+Description:
+       Get the device info for the specified device object
+
+--*/
+static void
+VmbusGetChannelInfo(
+       DEVICE_OBJECT   *DeviceObject,
+       DEVICE_INFO             *DeviceInfo
+       )
+{
+       GetChannelInfo(DeviceObject, DeviceInfo);
+}
+
+
+
+/*++
+
+Name:
+       VmbusCreateChildDevice()
+
+Description:
+       Creates the child device on the bus that represents the channel offer
+
+--*/
+
+DEVICE_OBJECT*
+VmbusChildDeviceCreate(
+       GUID DeviceType,
+       GUID DeviceInstance,
+       void *Context)
+{
+       VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
+
+       return vmbusDriver->OnChildDeviceCreate(
+               DeviceType,
+               DeviceInstance,
+               Context);
+}
+
+
+/*++
+
+Name:
+       VmbusChildDeviceAdd()
+
+Description:
+       Registers the child device with the vmbus
+
+--*/
+int
+VmbusChildDeviceAdd(
+   DEVICE_OBJECT* ChildDevice)
+{
+       VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
+
+       return vmbusDriver->OnChildDeviceAdd(gDevice, ChildDevice);
+}
+
+
+/*++
+
+Name:
+       VmbusChildDeviceRemove()
+
+Description:
+       Unregisters the child device from the vmbus
+
+--*/
+void
+VmbusChildDeviceRemove(
+   DEVICE_OBJECT* ChildDevice)
+{
+       VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
+
+       vmbusDriver->OnChildDeviceRemove(ChildDevice);
+}
+
+/*++
+
+Name:
+       VmbusChildDeviceDestroy()
+
+Description:
+       Release the child device from the vmbus
+
+--*/
+//void
+//VmbusChildDeviceDestroy(
+//     DEVICE_OBJECT* ChildDevice
+//     )
+//{
+//     VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
+//
+//     vmbusDriver->OnChildDeviceDestroy(ChildDevice);
+//}
+
+/*++
+
+Name:
+       VmbusOnDeviceAdd()
+
+Description:
+       Callback when the root bus device is added
+
+--*/
+static int
+VmbusOnDeviceAdd(
+       DEVICE_OBJECT   *dev,
+       void                    *AdditionalInfo
+       )
+{
+       UINT32 *irqvector = (UINT32*) AdditionalInfo;
+       int ret=0;
+
+       DPRINT_ENTER(VMBUS);
+
+       gDevice = dev;
+
+       memcpy(&gDevice->deviceType, &gVmbusDeviceType, sizeof(GUID));
+       memcpy(&gDevice->deviceInstance, &gVmbusDeviceId, sizeof(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;
+}
+
+
+/*++
+
+Name:
+       VmbusOnDeviceRemove()
+
+Description:
+       Callback when the root bus device is removed
+
+--*/
+int VmbusOnDeviceRemove(
+       DEVICE_OBJECT* dev
+       )
+{
+       int ret=0;
+
+       DPRINT_ENTER(VMBUS);
+
+       VmbusChannelReleaseUnattachedChannels();
+
+       VmbusDisconnect();
+
+       HvSynicCleanup();
+
+       DPRINT_EXIT(VMBUS);
+
+       return ret;
+}
+
+
+/*++
+
+Name:
+       VmbusOnCleanup()
+
+Description:
+       Perform any cleanup when the driver is removed
+
+--*/
+void
+VmbusOnCleanup(
+       DRIVER_OBJECT* drv
+       )
+{
+       //VMBUS_DRIVER_OBJECT* driver = (VMBUS_DRIVER_OBJECT*)drv;
+
+       DPRINT_ENTER(VMBUS);
+
+       HvCleanup();
+
+       DPRINT_EXIT(VMBUS);
+}
+
+
+/*++
+
+Name:
+       VmbusOnMsgDPC()
+
+Description:
+       DPC routine to handle messages from the hypervisior
+
+--*/
+void
+VmbusOnMsgDPC(
+       DRIVER_OBJECT* drv
+       )
+{
+       void *page_addr = gHvContext.synICMessagePage[0];
+
+       HV_MESSAGE* msg = (HV_MESSAGE*)page_addr + VMBUS_MESSAGE_SINT;
+       HV_MESSAGE *copied;
+       while (1)
+       {
+               if (msg->Header.MessageType == HvMessageTypeNone) // no msg
+               {
+                       break;
+               }
+               else
+               {
+                       copied = MemAllocAtomic(sizeof(HV_MESSAGE));
+                       if (copied == NULL)
+                       {
+                               continue;
+                       }
+
+                       memcpy(copied, msg, sizeof(HV_MESSAGE));
+                       WorkQueueQueueWorkItem(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
+               MemoryFence();
+
+               if (msg->Header.MessageFlags.MessagePending)
+               {
+                       // This will cause message queue rescan to possibly deliver another msg from the hypervisor
+                       WriteMsr(HV_X64_MSR_EOM, 0);
+               }
+       }
+}
+
+/*++
+
+Name:
+       VmbusOnEventDPC()
+
+Description:
+       DPC routine to handle events from the hypervisior
+
+--*/
+void
+VmbusOnEventDPC(
+       DRIVER_OBJECT* drv
+       )
+{
+       // TODO: Process any events
+       VmbusOnEvents();
+}
+
+
+/*++
+
+Name:
+       VmbusOnISR()
+
+Description:
+       ISR routine
+
+--*/
+int
+VmbusOnISR(
+       DRIVER_OBJECT* drv
+       )
+{
+       //VMBUS_DRIVER_OBJECT* driver = (VMBUS_DRIVER_OBJECT*)drv;
+
+       int ret=0;
+       //struct page* page;
+       void *page_addr;
+       HV_MESSAGE* msg;
+       HV_SYNIC_EVENT_FLAGS* event;
+
+       //page = SynICMessagePage[0];
+       //page_addr = page_address(page);
+       page_addr = gHvContext.synICMessagePage[0];
+       msg = (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 = (HV_SYNIC_EVENT_FLAGS*)page_addr + VMBUS_MESSAGE_SINT;
+
+       // Since we are a child, we only need to check bit 0
+       if (BitTestAndClear(&event->Flags32[0], 0))
+       {
+               DPRINT_DBG(VMBUS, "received event %d", event->Flags32[0]);
+               ret |= 0x2;
+       }
+
+       DPRINT_EXIT(VMBUS);
+       return ret;
+}
+
+// eof
diff --git a/drivers/staging/hv/VmbusPrivate.h b/drivers/staging/hv/VmbusPrivate.h
new file mode 100644 (file)
index 0000000..5e86165
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ *
+ * 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_
+
+#ifndef INTERNAL
+#define INTERNAL static
+#endif
+
+#include "Hv.h"
+#include "VmbusApi.h"
+#include "Channel.h"
+#include "ChannelMgmt.h"
+#include "ChannelInterface.h"
+//#include "ChannelMessages.h"
+#include "RingBuffer.h"
+//#include "Packet.h"
+#include "List.h"
+
+//
+// Defines
+//
+
+// 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
+
+//
+// Data types
+//
+
+typedef enum {
+       Disconnected,
+       Connecting,
+       Connected,
+       Disconnecting
+} VMBUS_CONNECT_STATE;
+
+#define MAX_SIZE_CHANNEL_MESSAGE                       HV_MESSAGE_PAYLOAD_BYTE_COUNT
+
+typedef struct _VMBUS_CONNECTION {
+
+       VMBUS_CONNECT_STATE                                     ConnectState;
+
+       UINT32                                                          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;
+       LIST_ENTRY                                                      ChannelMsgList;
+       HANDLE                                                          ChannelMsgLock;
+
+       // List of channels
+       LIST_ENTRY                                                      ChannelList;
+       HANDLE                                                          ChannelLock;
+
+       HANDLE                                                          WorkQueue;
+} VMBUS_CONNECTION;
+
+
+typedef struct _VMBUS_MSGINFO {
+       // Bookkeeping stuff
+       LIST_ENTRY                      MsgListEntry;
+
+       // Synchronize the request/response if needed
+       HANDLE                          WaitEvent;
+
+       // The message itself
+       unsigned char           Msg[0];
+} VMBUS_MSGINFO;
+
+
+//
+// Externs
+//
+extern VMBUS_CONNECTION gVmbusConnection;
+
+//
+// General vmbus interface
+//
+INTERNAL DEVICE_OBJECT*
+VmbusChildDeviceCreate(
+       GUID deviceType,
+       GUID deviceInstance,
+       void *context);
+
+INTERNAL int
+VmbusChildDeviceAdd(
+       DEVICE_OBJECT* Device);
+
+INTERNAL void
+VmbusChildDeviceRemove(
+   DEVICE_OBJECT* Device);
+
+//INTERNAL void
+//VmbusChildDeviceDestroy(
+//     DEVICE_OBJECT*);
+
+INTERNAL VMBUS_CHANNEL*
+GetChannelFromRelId(
+       UINT32 relId
+       );
+
+//
+// Connection interface
+//
+INTERNAL int
+VmbusConnect(
+       VOID
+       );
+
+INTERNAL int
+VmbusDisconnect(
+       VOID
+       );
+
+INTERNAL int
+VmbusPostMessage(
+       PVOID                   buffer,
+       SIZE_T                  bufSize
+       );
+
+INTERNAL int
+VmbusSetEvent(
+       UINT32 childRelId
+       );
+
+INTERNAL VOID
+VmbusOnEvents(
+  VOID
+       );
+
+
+#endif // _VMBUS_PRIVATE_H_
diff --git a/drivers/staging/hv/osd.c b/drivers/staging/hv/osd.c
new file mode 100644 (file)
index 0000000..8388525
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ *
+ * 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/config.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/timer.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/kmap_types.h>
+#include <asm/atomic.h>
+
+#include "osd.h"
+
+//
+// Data types
+//
+typedef struct _TIMER {
+       struct timer_list timer;
+       PFN_TIMER_CALLBACK callback;
+       void* context;
+}TIMER;
+
+
+typedef struct _WAITEVENT {
+       int     condition;
+       wait_queue_head_t event;
+} WAITEVENT;
+
+typedef struct _SPINLOCK {
+       spinlock_t              lock;
+       unsigned long   flags;
+} SPINLOCK;
+
+typedef struct _WORKQUEUE {
+       struct workqueue_struct *queue;
+} WORKQUEUE;
+
+typedef struct _WORKITEM {
+       struct work_struct work;
+       PFN_WORKITEM_CALLBACK callback;
+       void* context;
+} WORKITEM;
+
+
+//
+// Global
+//
+
+void LogMsg(const char *fmt, ...)
+{
+#ifdef KERNEL_2_6_5
+       char buf[1024];
+#endif
+       va_list args;
+
+       va_start(args, fmt);
+#ifdef KERNEL_2_6_5
+       vsnprintf(buf, 1024, fmt, args);
+       va_end(args);
+       printk(buf);
+#else
+       vprintk(fmt, args);
+       va_end(args);
+#endif
+}
+
+void BitSet(unsigned int* addr, int bit)
+{
+       set_bit(bit, (unsigned long*)addr);
+}
+
+int BitTest(unsigned int* addr, int bit)
+{
+       return test_bit(bit, (unsigned long*)addr);
+}
+
+void BitClear(unsigned int* addr, int bit)
+{
+       clear_bit(bit, (unsigned long*)addr);
+}
+
+int BitTestAndClear(unsigned int* addr, int bit)
+{
+       return test_and_clear_bit(bit, (unsigned long*)addr);
+}
+
+int BitTestAndSet(unsigned int* addr, int bit)
+{
+       return test_and_set_bit(bit, (unsigned long*)addr);
+}
+
+
+int InterlockedIncrement(int *val)
+{
+#ifdef KERNEL_2_6_5
+       int i;
+       local_irq_disable();
+       i = atomic_read((atomic_t*)val);
+       atomic_set((atomic_t*)val, i+1);
+       local_irq_enable();
+       return i+1;
+#else
+       return atomic_inc_return((atomic_t*)val);
+#endif
+}
+
+int InterlockedDecrement(int *val)
+{
+#ifdef KERNEL_2_6_5
+       int i;
+       local_irq_disable();
+       i = atomic_read((atomic_t*)val);
+       atomic_set((atomic_t*)val, i-1);
+       local_irq_enable();
+       return i-1;
+#else
+       return atomic_dec_return((atomic_t*)val);
+#endif
+}
+
+#ifndef atomic_cmpxchg
+#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
+#endif
+int InterlockedCompareExchange(int *val, int new, int curr)
+{
+       //return ((int)cmpxchg(((atomic_t*)val), curr, new));
+       return atomic_cmpxchg((atomic_t*)val, curr, new);
+
+}
+
+void Sleep(unsigned long usecs)
+{
+       udelay(usecs);
+}
+
+void* 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 VirtualFree(void* VirtAddr)
+{
+       return vfree(VirtAddr);
+}
+
+void* 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;
+}
+
+void 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);*/
+}
+
+
+void* PageMapVirtualAddress(unsigned long Pfn)
+{
+       return kmap_atomic(pfn_to_page(Pfn), KM_IRQ0);
+}
+
+void PageUnmapVirtualAddress(void* VirtAddr)
+{
+       kunmap_atomic(VirtAddr, KM_IRQ0);
+}
+
+void* MemAlloc(unsigned int size)
+{
+       return kmalloc(size, GFP_KERNEL);
+}
+
+void* MemAllocZeroed(unsigned int size)
+{
+       void *p = kmalloc(size, GFP_KERNEL);
+       if (p) memset(p, 0, size);
+       return p;
+}
+
+void* MemAllocAtomic(unsigned int size)
+{
+       return kmalloc(size, GFP_ATOMIC);
+}
+
+void MemFree(void* buf)
+{
+       kfree(buf);
+}
+
+void *MemMapIO(unsigned long phys, unsigned long size)
+{
+#if X2V_LINUX
+#ifdef __x86_64__
+       return (void*)(phys + 0xFFFF83000C000000);
+#else // i386
+       return (void*)(phys + 0xfb000000);
+#endif
+#else
+       return (void*)GetVirtualAddress(phys); //return ioremap_nocache(phys, size);
+#endif
+}
+
+void MemUnmapIO(void *virt)
+{
+       //iounmap(virt);
+}
+
+void MemoryFence()
+{
+       mb();
+}
+
+void TimerCallback(unsigned long data)
+{
+       TIMER* t = (TIMER*)data;
+
+       t->callback(t->context);
+}
+
+HANDLE TimerCreate(PFN_TIMER_CALLBACK pfnTimerCB, void* context)
+{
+       TIMER* t = kmalloc(sizeof(TIMER), GFP_KERNEL);
+       if (!t)
+       {
+               return NULL;
+       }
+
+       t->callback = pfnTimerCB;
+       t->context = context;
+
+       init_timer(&t->timer);
+       t->timer.data = (unsigned long)t;
+       t->timer.function = TimerCallback;
+
+       return t;
+}
+
+void TimerStart(HANDLE hTimer, UINT32 expirationInUs)
+{
+       TIMER* t  = (TIMER* )hTimer;
+
+       t->timer.expires = jiffies + usecs_to_jiffies(expirationInUs);
+       add_timer(&t->timer);
+}
+
+int TimerStop(HANDLE hTimer)
+{
+       TIMER* t  = (TIMER* )hTimer;
+
+       return del_timer(&t->timer);
+}
+
+void TimerClose(HANDLE hTimer)
+{
+       TIMER* t  = (TIMER* )hTimer;
+
+       del_timer(&t->timer);
+       kfree(t);
+}
+
+SIZE_T GetTickCount(void)
+{
+       return jiffies;
+}
+
+signed long long GetTimestamp(void)
+{
+       struct timeval t;
+
+       do_gettimeofday(&t);
+
+       return  timeval_to_ns(&t);
+}
+
+HANDLE WaitEventCreate(void)
+{
+       WAITEVENT* wait = kmalloc(sizeof(WAITEVENT), GFP_KERNEL);
+       if (!wait)
+       {
+               return NULL;
+       }
+
+       wait->condition = 0;
+       init_waitqueue_head(&wait->event);
+       return wait;
+}
+
+void WaitEventClose(HANDLE hWait)
+{
+       WAITEVENT* waitEvent = (WAITEVENT* )hWait;
+       kfree(waitEvent);
+}
+
+void WaitEventSet(HANDLE hWait)
+{
+       WAITEVENT* waitEvent = (WAITEVENT* )hWait;
+       waitEvent->condition = 1;
+       wake_up_interruptible(&waitEvent->event);
+}
+
+int WaitEventWait(HANDLE hWait)
+{
+       int ret=0;
+       WAITEVENT* waitEvent = (WAITEVENT* )hWait;
+
+       ret= wait_event_interruptible(waitEvent->event,
+               waitEvent->condition);
+       waitEvent->condition = 0;
+       return ret;
+}
+
+int WaitEventWaitEx(HANDLE hWait, UINT32 TimeoutInMs)
+{
+       int ret=0;
+       WAITEVENT* waitEvent = (WAITEVENT* )hWait;
+
+       ret= wait_event_interruptible_timeout(waitEvent->event,
+                                                                                       waitEvent->condition,
+                                                                                       msecs_to_jiffies(TimeoutInMs));
+       waitEvent->condition = 0;
+       return ret;
+}
+
+HANDLE SpinlockCreate(VOID)
+{
+       SPINLOCK* spin = kmalloc(sizeof(SPINLOCK), GFP_KERNEL);
+       if (!spin)
+       {
+               return NULL;
+       }
+       spin_lock_init(&spin->lock);
+
+       return spin;
+}
+
+VOID SpinlockAcquire(HANDLE hSpin)
+{
+       SPINLOCK* spin = (SPINLOCK* )hSpin;
+
+       spin_lock_irqsave(&spin->lock, spin->flags);
+}
+
+VOID SpinlockRelease(HANDLE hSpin)
+{
+       SPINLOCK* spin = (SPINLOCK* )hSpin;
+
+       spin_unlock_irqrestore(&spin->lock, spin->flags);
+}
+
+VOID SpinlockClose(HANDLE hSpin)
+{
+       SPINLOCK* spin = (SPINLOCK* )hSpin;
+       kfree(spin);
+}
+
+void* Physical2LogicalAddr(ULONG_PTR PhysAddr)
+{
+       void* logicalAddr = phys_to_virt(PhysAddr);
+       BUG_ON(!virt_addr_valid(logicalAddr));
+       return logicalAddr;
+}
+
+ULONG_PTR Logical2PhysicalAddr(PVOID LogicalAddr)
+{
+       BUG_ON(!virt_addr_valid(LogicalAddr));
+       return virt_to_phys(LogicalAddr);
+}
+
+
+ULONG_PTR Virtual2Physical(PVOID VirtAddr)
+{
+       ULONG_PTR pfn = vmalloc_to_pfn(VirtAddr);
+
+       return pfn << PAGE_SHIFT;
+}
+
+#ifdef KERNEL_2_6_27
+void WorkItemCallback(struct work_struct *work)
+#else
+void WorkItemCallback(void* work)
+#endif
+{
+       WORKITEM* w = (WORKITEM*)work;
+
+       w->callback(w->context);
+
+       kfree(w);
+}
+
+HANDLE WorkQueueCreate(char* name)
+{
+       WORKQUEUE *wq = kmalloc(sizeof(WORKQUEUE), GFP_KERNEL);
+       if (!wq)
+       {
+               return NULL;
+       }
+       wq->queue = create_workqueue(name);
+
+       return wq;
+}
+
+void WorkQueueClose(HANDLE hWorkQueue)
+{
+       WORKQUEUE *wq = (WORKQUEUE *)hWorkQueue;
+
+       destroy_workqueue(wq->queue);
+
+       return;
+}
+
+int WorkQueueQueueWorkItem(HANDLE hWorkQueue, PFN_WORKITEM_CALLBACK workItem, void* context)
+{
+       WORKQUEUE *wq = (WORKQUEUE *)hWorkQueue;
+
+       WORKITEM* w = kmalloc(sizeof(WORKITEM), GFP_ATOMIC);
+       if (!w)
+       {
+               return -1;
+       }
+
+       w->callback = workItem,
+       w->context = context;
+#ifdef KERNEL_2_6_27
+       INIT_WORK(&w->work, WorkItemCallback);
+#else
+       INIT_WORK(&w->work, WorkItemCallback, w);
+#endif
+       return queue_work(wq->queue, &w->work);
+}
+
+void QueueWorkItem(PFN_WORKITEM_CALLBACK workItem, void* context)
+{
+       WORKITEM* w = kmalloc(sizeof(WORKITEM), GFP_ATOMIC);
+       if (!w)
+       {
+               return;
+       }
+
+       w->callback = workItem,
+       w->context = context;
+#ifdef KERNEL_2_6_27
+       INIT_WORK(&w->work, WorkItemCallback);
+#else
+       INIT_WORK(&w->work, WorkItemCallback, w);
+#endif
+       schedule_work(&w->work);
+}
diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c
new file mode 100644 (file)
index 0000000..0acf42c
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ *
+ * 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 "logging.h"
+#include "vmbus.h"
+
+//
+// Defines
+//
+
+// FIXME! We need to do this dynamically for PIC and APIC system
+#define VMBUS_IRQ                              0x5
+#ifdef KERNEL_2_6_27
+#define VMBUS_IRQ_VECTOR     IRQ5_VECTOR
+#endif
+//
+// Data types
+//
+
+// Main vmbus driver data structure
+struct vmbus_driver_context {
+       // !! These must be the first 2 fields !!
+       // The driver field is not used in here. Instead, the bus field is
+       // used to represent the driver
+       struct driver_context   drv_ctx;
+       VMBUS_DRIVER_OBJECT             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 decl
+//
+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);
+#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
+#elif defined(KERNEL_2_6_27)
+static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env);
+#else
+static int vmbus_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size);
+#endif
+static void vmbus_msg_dpc(unsigned long data);
+static void vmbus_event_dpc(unsigned long data);
+
+#ifdef KERNEL_2_6_27
+static irqreturn_t vmbus_isr(int irq, void* dev_id);
+#else
+static int vmbus_isr(int irq, void* dev_id, struct pt_regs *regs);
+#endif
+
+static void vmbus_device_release(struct device *device);
+static void vmbus_bus_release(struct device *device);
+
+static DEVICE_OBJECT* vmbus_child_device_create(GUID type, GUID instance, void* context);
+static void vmbus_child_device_destroy(DEVICE_OBJECT* device_obj);
+static int vmbus_child_device_register(DEVICE_OBJECT* root_device_obj, DEVICE_OBJECT* child_device_obj);
+static void vmbus_child_device_unregister(DEVICE_OBJECT* child_device_obj);
+static void vmbus_child_device_get_info(DEVICE_OBJECT *device_obj, DEVICE_INFO *device_info);
+
+//static ssize_t vmbus_show_class_id(struct device *dev, struct device_attribute *attr, char *buf);
+//static ssize_t vmbus_show_device_id(struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t vmbus_show_device_attr(struct device *dev, struct device_attribute *dev_attr, char *buf);
+
+//
+// Global
+//
+
+// Global logging setting
+
+//unsigned int vmbus_loglevel= (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT);
+//unsigned int vmbus_loglevel= (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT);
+unsigned int vmbus_loglevel= (ALL_MODULES << 16 | INFO_LVL);
+EXPORT_SYMBOL(vmbus_loglevel);
+
+static int vmbus_irq = VMBUS_IRQ;
+
+// Setup /proc/sys/bus/vmbus/vmbus_loglevel
+// Allow usage of sysctl cmd to set the logging level
+static struct ctl_table_header *vmbus_ctl_table_hdr;
+
+static ctl_table vmbus_dev_ctl_table[] = {
+       { .ctl_name     = 8461,
+         .procname     = "vmbus_loglevel",
+         .data         = &vmbus_loglevel,
+         .maxlen       = sizeof(vmbus_loglevel),
+         .mode         = 0644,
+         .proc_handler = &proc_dointvec },
+       { }
+};
+
+static ctl_table vmbus_ctl_table[] = {
+       { .ctl_name     = CTL_DEV,
+         .procname     = "vmbus",
+         .mode         = 0555,
+         .child        = vmbus_dev_ctl_table },
+       { }
+};
+
+static ctl_table vmus_root_ctl_table[] = {
+       { .ctl_name     = CTL_BUS,
+         .procname     = "bus",
+         .mode         = 0555,
+         .child        = vmbus_ctl_table },
+       { }
+};
+
+#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
+#else
+//
+// 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
+};
+#endif
+
+// The one and only one
+static struct vmbus_driver_context g_vmbus_drv={
+       .bus.name       = "vmbus",
+       .bus.match      = vmbus_match,
+#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
+#else
+       .bus.shutdown = vmbus_shutdown,
+       .bus.remove = vmbus_remove,
+       .bus.probe      = vmbus_probe,
+       .bus.uevent = vmbus_uevent,
+       .bus.dev_attrs = vmbus_device_attrs,
+#endif
+};
+
+//
+// Routines
+//
+
+
+/*++
+
+Name:  vmbus_show_device_attr()
+
+Desc:  Show the device attribute in sysfs. This is invoked when user does a "cat /sys/bus/vmbus/devices/<bus device>/<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);
+       DEVICE_INFO device_info;
+
+       memset(&device_info, 0, sizeof(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;
+       }
+}
+
+/*++
+
+Name:  vmbus_show_class_id()
+
+Desc:  Show the device class id in sysfs
+
+--*/
+//static ssize_t vmbus_show_class_id(struct device *dev, struct device_attribute *attr, char *buf)
+//{
+//     struct device_context *device_ctx = device_to_device_context(dev);
+//     return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
+//             device_ctx->class_id[3], device_ctx->class_id[2], device_ctx->class_id[1], device_ctx->class_id[0],
+//             device_ctx->class_id[5], device_ctx->class_id[4],
+//             device_ctx->class_id[7], device_ctx->class_id[6],
+//             device_ctx->class_id[8], device_ctx->class_id[9], device_ctx->class_id[10], device_ctx->class_id[11], device_ctx->class_id[12], device_ctx->class_id[13], device_ctx->class_id[14], device_ctx->class_id[15]);
+//}
+
+/*++
+
+Name:  vmbus_show_device_id()
+
+Desc:  Show the device instance id in sysfs
+
+--*/
+//static ssize_t vmbus_show_device_id(struct device *dev, struct device_attribute *attr, char *buf)
+//{
+//     struct device_context *device_ctx = device_to_device_context(dev);
+//     return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
+//             device_ctx->device_id[3], device_ctx->device_id[2], device_ctx->device_id[1], device_ctx->device_id[0],
+//             device_ctx->device_id[5], device_ctx->device_id[4],
+//             device_ctx->device_id[7], device_ctx->device_id[6],
+//             device_ctx->device_id[8], device_ctx->device_id[9], device_ctx->device_id[10], device_ctx->device_id[11], device_ctx->device_id[12], device_ctx->device_id[13], device_ctx->device_id[14], device_ctx->device_id[15]);
+//}
+
+/*++
+
+Name:  vmbus_bus_init()
+
+Desc:  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
+--*/
+int vmbus_bus_init(PFN_DRIVERINITIALIZE pfn_drv_init)
+{
+       int ret=0;
+       unsigned int vector=0;
+
+       struct vmbus_driver_context *vmbus_drv_ctx=&g_vmbus_drv;
+       VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
+
+       struct device_context *dev_ctx=&g_vmbus_drv.device_ctx;
+
+       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 = pfn_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
+       bus_register(&vmbus_drv_ctx->bus);
+
+       // Get the interrupt resource
+#ifdef KERNEL_2_6_27
+       ret = request_irq(vmbus_irq,
+                         vmbus_isr,
+                         IRQF_SAMPLE_RANDOM,
+                         vmbus_drv_obj->Base.name,
+                         NULL);
+#else
+       ret = request_irq(vmbus_irq,
+                         vmbus_isr,
+                         SA_SAMPLE_RANDOM,
+                         vmbus_drv_obj->Base.name,
+                         NULL);
+#endif
+
+       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;
+       }
+#ifdef KERNEL_2_6_27
+       vector = VMBUS_IRQ_VECTOR;
+#else
+#if X2V_LINUX
+       vector = vmbus_irq + FIRST_DEVICE_VECTOR - 2;
+#else
+       vector = vmbus_irq + FIRST_EXTERNAL_VECTOR;
+#endif
+#endif
+
+       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);
+       sprintf(dev_ctx->device.bus_id, "vmbus_0_0");
+       memcpy(&dev_ctx->class_id, &dev_ctx->device_obj.deviceType, sizeof(GUID));
+       memcpy(&dev_ctx->device_id, &dev_ctx->device_obj.deviceInstance, sizeof(GUID));
+
+       // No need to bind a driver to the root device.
+       dev_ctx->device.parent = NULL;
+       dev_ctx->device.bus = &vmbus_drv_ctx->bus; //NULL; // vmbus_remove() does not get invoked
+
+       // Setup the device dispatch table
+       dev_ctx->device.release = vmbus_bus_release;
+
+       // Setup the bus as root device
+       device_register(&dev_ctx->device);
+
+       vmbus_drv_obj->GetChannelOffers();
+
+cleanup:
+       DPRINT_EXIT(VMBUS_DRV);
+
+       return ret;
+}
+
+
+/*++
+
+Name:  vmbus_bus_exit()
+
+Desc:  Terminate the vmbus driver. This routine is opposite of vmbus_bus_init()
+
+--*/
+void vmbus_bus_exit(void)
+{
+       VMBUS_DRIVER_OBJECT *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;
+}
+
+/*++
+
+Name:  vmbus_child_driver_register()
+
+Desc:  Register a vmbus's child driver
+
+--*/
+void vmbus_child_driver_register(struct driver_context* driver_ctx)
+{
+       VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
+
+       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;
+
+       driver_register(&driver_ctx->driver);
+
+       vmbus_drv_obj->GetChannelOffers();
+
+       DPRINT_EXIT(VMBUS_DRV);
+}
+
+EXPORT_SYMBOL(vmbus_child_driver_register);
+
+/*++
+
+Name:  vmbus_child_driver_unregister()
+
+Desc:  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);
+
+/*++
+
+Name:  vmbus_get_interface()
+
+Desc:  Get the vmbus channel interface. This is invoked by child/client driver that sits
+               above vmbus
+--*/
+void vmbus_get_interface(VMBUS_CHANNEL_INTERFACE *interface)
+{
+       VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
+
+       vmbus_drv_obj->GetChannelInterface(interface);
+}
+
+EXPORT_SYMBOL(vmbus_get_interface);
+
+
+/*++
+
+Name:  vmbus_child_device_get_info()
+
+Desc:  Get the vmbus child device info. This is invoked to display various device attributes in sysfs.
+--*/
+static void vmbus_child_device_get_info(DEVICE_OBJECT *device_obj, DEVICE_INFO *device_info)
+{
+       VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
+
+       vmbus_drv_obj->GetChannelInfo(device_obj, device_info);
+}
+
+
+/*++
+
+Name:  vmbus_child_device_create()
+
+Desc:  Creates and registers a new child device on the vmbus.
+
+--*/
+static DEVICE_OBJECT* vmbus_child_device_create(GUID type, GUID instance, void* context)
+{
+       struct device_context *child_device_ctx;
+       DEVICE_OBJECT* 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(GUID));
+       memcpy(&child_device_obj->deviceInstance, &instance, sizeof(GUID));
+
+       memcpy(&child_device_ctx->class_id, &type, sizeof(GUID));
+       memcpy(&child_device_ctx->device_id, &instance, sizeof(GUID));
+
+       DPRINT_EXIT(VMBUS_DRV);
+
+       return child_device_obj;
+}
+
+/*++
+
+Name:  vmbus_child_device_register()
+
+Desc:  Register the child device on the specified bus
+
+--*/
+static int vmbus_child_device_register(DEVICE_OBJECT* root_device_obj, DEVICE_OBJECT* 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 int device_num=0;
+
+       DPRINT_ENTER(VMBUS_DRV);
+
+       DPRINT_DBG(VMBUS_DRV, "child device (%p) registering", child_device_ctx);
+       //
+       // Make sure we are not registered already
+       //
+       if (child_device_ctx->device.bus_id[0] != '\0')
+       {
+               DPRINT_ERR(VMBUS_DRV, "child device (%p) already registered - busid %s", child_device_ctx, child_device_ctx->device.bus_id);
+
+               ret = -1;
+               goto Cleanup;
+       }
+
+       // Set the device bus id. Otherwise, device_register()will fail.
+       sprintf(child_device_ctx->device.bus_id, "vmbus_0_%d", InterlockedIncrement(&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) (%d)", &child_device_ctx->device);
+       else
+               DPRINT_INFO(VMBUS_DRV, "child device (%p) registered", &child_device_ctx->device);
+
+Cleanup:
+       DPRINT_EXIT(VMBUS_DRV);
+
+       return ret;
+}
+
+/*++
+
+Name:  vmbus_child_device_unregister()
+
+Desc:  Remove the specified child device from the vmbus.
+
+--*/
+static void vmbus_child_device_unregister(DEVICE_OBJECT* 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);
+}
+
+
+/*++
+
+Name:  vmbus_child_device_destroy()
+
+Desc:  Destroy the specified child device on the vmbus.
+
+--*/
+static void vmbus_child_device_destroy(DEVICE_OBJECT* device_obj)
+{
+       DPRINT_ENTER(VMBUS_DRV);
+
+       DPRINT_EXIT(VMBUS_DRV);
+}
+
+/*++
+
+Name:  vmbus_uevent()
+
+Desc:  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
+
+--*/
+#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
+#elif defined(KERNEL_2_6_27)
+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;
+}
+
+#else
+static int vmbus_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size)
+{
+       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]);
+
+       ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+               "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(envp, num_envp, &i, buffer, buffer_size, &len,
+               "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;
+       }
+
+       envp[i] = NULL;
+
+       DPRINT_EXIT(VMBUS_DRV);
+
+       return 0;
+}
+#endif
+
+/*++
+
+Name:  vmbus_match()
+
+Desc:  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(GUID)) == 0)
+       {
+               // !! NOTE: The driver_ctx is not a vmbus_drv_ctx. We typecast it here to access the
+               // DRIVER_OBJECT 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;
+}
+
+
+/*++
+
+Name:  vmbus_probe_failed_cb()
+
+Desc:  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()
+--*/
+#ifdef KERNEL_2_6_27
+static void vmbus_probe_failed_cb(struct work_struct *context)
+#else
+static void vmbus_probe_failed_cb(void* context)
+#endif
+{
+       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);
+}
+
+
+/*++
+
+Name:  vmbus_probe()
+
+Desc:  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)...", child_device->bus_id, child_device, child_device->driver->name, ret);
+
+#ifdef KERNEL_2_6_27
+                       INIT_WORK(&device_ctx->probe_failed_work_item, vmbus_probe_failed_cb);
+#else
+                       INIT_WORK(&device_ctx->probe_failed_work_item, vmbus_probe_failed_cb, device_ctx);
+#endif
+                       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;
+}
+
+
+/*++
+
+Name:  vmbus_remove()
+
+Desc:  Remove a vmbus device
+
+--*/
+static int vmbus_remove(struct device *child_device)
+{
+       int ret=0;
+       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;
+               }
+       }
+       else
+       {
+
+       }
+
+       DPRINT_EXIT(VMBUS_DRV);
+
+       return 0;
+}
+
+/*++
+
+Name:  vmbus_shutdown()
+
+Desc:  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;
+}
+
+/*++
+
+Name:  vmbus_bus_release()
+
+Desc:  Final callback release of the vmbus root device
+
+--*/
+static void vmbus_bus_release(struct device *device)
+{
+       DPRINT_ENTER(VMBUS_DRV);
+       DPRINT_EXIT(VMBUS_DRV);
+}
+
+/*++
+
+Name:  vmbus_device_release()
+
+Desc:  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;
+}
+
+/*++
+
+Name:  vmbus_msg_dpc()
+
+Desc:  Tasklet routine to handle hypervisor messages
+
+--*/
+static void vmbus_msg_dpc(unsigned long data)
+{
+       VMBUS_DRIVER_OBJECT* vmbus_drv_obj = (VMBUS_DRIVER_OBJECT*)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);
+}
+
+/*++
+
+Name:  vmbus_msg_dpc()
+
+Desc:  Tasklet routine to handle hypervisor events
+
+--*/
+static void vmbus_event_dpc(unsigned long data)
+{
+       VMBUS_DRIVER_OBJECT* vmbus_drv_obj = (VMBUS_DRIVER_OBJECT*)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);
+}
+
+/*++
+
+Name:  vmbus_msg_dpc()
+
+Desc:  ISR routine
+
+--*/
+#ifdef KERNEL_2_6_27
+static irqreturn_t vmbus_isr(int irq, void* dev_id)
+#else
+static int vmbus_isr(int irq, void* dev_id, struct pt_regs *regs)
+#endif
+{
+       int ret=0;
+       VMBUS_DRIVER_OBJECT* vmbus_driver_obj = &g_vmbus_drv.drv_obj;
+
+       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;
+       }
+}
+
+MODULE_LICENSE("GPL");
+
+
+/*++
+
+Name:  vmbus_init()
+
+Desc:  Main vmbus driver entry routine
+
+--*/
+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));
+#ifdef KERNEL_2_6_27
+//Todo: it is used for loglevel, to be ported to new kernel.
+#else
+       vmbus_ctl_table_hdr = register_sysctl_table(vmus_root_ctl_table, 0);
+       if (!vmbus_ctl_table_hdr)
+       {
+               DPRINT_EXIT(VMBUS_DRV);
+               return -ENOMEM;
+       }
+#endif
+
+       ret = vmbus_bus_init(VmbusInitialize);
+
+       DPRINT_EXIT(VMBUS_DRV);
+       return ret;
+}
+
+
+
+/*++
+
+Name:  vmbus_init()
+
+Desc:  Main vmbus driver exit routine
+
+--*/
+static void __exit vmbus_exit(void)
+{
+       DPRINT_ENTER(VMBUS_DRV);
+
+       vmbus_bus_exit();
+#ifdef KERNEL_2_6_27
+//Todo: it is used for loglevel, to be ported to new kernel.
+#else
+       unregister_sysctl_table(vmbus_ctl_table_hdr);
+#endif
+       DPRINT_EXIT(VMBUS_DRV);
+
+       return;
+}
+
+#if defined(KERNEL_2_6_5)
+#else
+module_param(vmbus_irq, int, S_IRUGO);
+module_param(vmbus_loglevel, int, S_IRUGO);
+#endif
+
+module_init(vmbus_init);
+module_exit(vmbus_exit);
+// eof