mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-10-31 02:05:04 +00:00
Before this commit each WDF driver called VirtIOWdfSetDriverFeatures from its PrepareHardware WDF callback. Many drivers do not have any specific needs for features but needed to make this call in order just to set VIRTIO_F_VERSION_1 and VIRTIO_F_ANY_LAYOUT properly. So, even when the call VirtIOWdfSetDriverFeatures is optional in general, it is mandatory to handle V1 devices. Current commit moves ack for VIRTIO_F_VERSION_1 and VIRTIO_F_ANY_LAYOUT features to WDF VirtIO library. If the driver does not call VirtIOWdfSetDriverFeatures, these features are set properly at time of queues' initialization. So, drivers need to make this call only if they need to enable some type-spefific features or if they need to disable some common features which WDF library handles automatically (currently VIRTIO_F_VERSION_1, VIRTIO_F_ANY_LAYOUT). Signed-off-by: Yuri Benditovich <[email protected]>
611 lines
18 KiB
C
611 lines
18 KiB
C
/*
|
|
* Device related functions
|
|
*
|
|
* Copyright (c) 2016-2017 Red Hat, Inc.
|
|
*
|
|
* Author(s):
|
|
* Ladi Prosek <[email protected]>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met :
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and / or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright holders nor the names of their contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include "vioinput.h"
|
|
|
|
#if defined(EVENT_TRACING)
|
|
#include "Device.tmh"
|
|
#endif
|
|
|
|
EVT_WDF_DEVICE_PREPARE_HARDWARE VIOInputEvtDevicePrepareHardware;
|
|
EVT_WDF_DEVICE_RELEASE_HARDWARE VIOInputEvtDeviceReleaseHardware;
|
|
EVT_WDF_DEVICE_D0_ENTRY VIOInputEvtDeviceD0Entry;
|
|
EVT_WDF_DEVICE_D0_EXIT VIOInputEvtDeviceD0Exit;
|
|
|
|
static NTSTATUS VIOInputInitInterruptHandling(IN WDFDEVICE hDevice);
|
|
static NTSTATUS VIOInputInitAllQueues(IN WDFOBJECT hDevice);
|
|
static VOID VIOInputShutDownAllQueues(IN WDFOBJECT WdfDevice);
|
|
static NTSTATUS VIOInputCreateChildPdo(IN WDFDEVICE hDevice);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (PAGE, VIOInputEvtDeviceAdd)
|
|
#pragma alloc_text (PAGE, VIOInputEvtDevicePrepareHardware)
|
|
#pragma alloc_text (PAGE, VIOInputEvtDeviceReleaseHardware)
|
|
#pragma alloc_text (PAGE, VIOInputCreateChildPdo)
|
|
#pragma alloc_text (PAGE, VIOInputEvtDeviceD0Exit)
|
|
#endif
|
|
|
|
static
|
|
NTSTATUS
|
|
VIOInputInitInterruptHandling(
|
|
IN WDFDEVICE hDevice)
|
|
{
|
|
WDF_INTERRUPT_CONFIG interruptConfig;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PINPUT_DEVICE pContext = GetDeviceContext(hDevice);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,
|
|
"Failed to create control queue interrupt: %x\n", status);
|
|
return status;
|
|
}
|
|
|
|
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
|
|
VIOInputInterruptIsr, VIOInputQueuesInterruptDpc);
|
|
|
|
interruptConfig.EvtInterruptEnable = VIOInputInterruptEnable;
|
|
interruptConfig.EvtInterruptDisable = VIOInputInterruptDisable;
|
|
|
|
status = WdfInterruptCreate(hDevice, &interruptConfig, WDF_NO_OBJECT_ATTRIBUTES,
|
|
&pContext->QueuesInterrupt);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,
|
|
"Failed to create general queue interrupt: %x\n", status);
|
|
return status;
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputEvtDeviceAdd(
|
|
IN WDFDRIVER Driver,
|
|
IN PWDFDEVICE_INIT DeviceInit)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
WDF_OBJECT_ATTRIBUTES Attributes;
|
|
WDFDEVICE hDevice;
|
|
WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
|
|
PINPUT_DEVICE pContext = NULL;
|
|
WDF_IO_QUEUE_CONFIG queueConfig;
|
|
|
|
UNREFERENCED_PARAMETER(Driver);
|
|
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__);
|
|
|
|
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
|
|
PnpPowerCallbacks.EvtDevicePrepareHardware = VIOInputEvtDevicePrepareHardware;
|
|
PnpPowerCallbacks.EvtDeviceReleaseHardware = VIOInputEvtDeviceReleaseHardware;
|
|
PnpPowerCallbacks.EvtDeviceD0Entry = VIOInputEvtDeviceD0Entry;
|
|
PnpPowerCallbacks.EvtDeviceD0Exit = VIOInputEvtDeviceD0Exit;
|
|
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks);
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, INPUT_DEVICE);
|
|
Attributes.SynchronizationScope = WdfSynchronizationScopeDevice;
|
|
status = WdfDeviceCreate(&DeviceInit, &Attributes, &hDevice);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceCreate failed - 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
status = VIOInputInitInterruptHandling(hDevice);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "VIOInputInitInterruptHandling failed - 0x%x\n", status);
|
|
}
|
|
|
|
status = WdfDeviceCreateDeviceInterface(
|
|
hDevice,
|
|
&GUID_VIOINPUT_CONTROLLER,
|
|
NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceCreateDeviceInterface failed - 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
pContext = GetDeviceContext(hDevice);
|
|
|
|
pContext->EventQMemBlock = pContext->StatusQMemBlock = NULL;
|
|
|
|
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
|
|
&queueConfig,
|
|
WdfIoQueueDispatchParallel);
|
|
|
|
queueConfig.EvtIoInternalDeviceControl = EvtIoDeviceControl;
|
|
|
|
status = WdfIoQueueCreate(
|
|
hDevice,
|
|
&queueConfig,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
&pContext->IoctlQueue);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate failed - 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
WDF_IO_QUEUE_CONFIG_INIT(
|
|
&queueConfig,
|
|
WdfIoQueueDispatchManual);
|
|
|
|
status = WdfIoQueueCreate(
|
|
hDevice,
|
|
&queueConfig,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
&pContext->HidQueue);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate failed - 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
|
|
Attributes.ParentObject = hDevice;
|
|
status = WdfSpinLockCreate(
|
|
&Attributes,
|
|
&pContext->EventQLock);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfSpinLockCreate failed - 0x%x\n", status);
|
|
return status;
|
|
}
|
|
status = WdfSpinLockCreate(
|
|
&Attributes,
|
|
&pContext->StatusQLock);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfSpinLockCreate failed - 0x%x\n", status);
|
|
return status;
|
|
}
|
|
|
|
RtlZeroMemory(&pContext->HidDeviceAttributes, sizeof(HID_DEVICE_ATTRIBUTES));
|
|
pContext->HidDeviceAttributes.Size = sizeof(HID_DEVICE_ATTRIBUTES);
|
|
pContext->HidDeviceAttributes.VendorID = HIDMINI_VID;
|
|
pContext->HidDeviceAttributes.ProductID = HIDMINI_PID;
|
|
pContext->HidDeviceAttributes.VersionNumber = HIDMINI_VERSION;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
static void VIOInputFreeMemBlocks(PINPUT_DEVICE pContext)
|
|
{
|
|
if (pContext->EventQMemBlock)
|
|
{
|
|
pContext->EventQMemBlock->destroy(pContext->EventQMemBlock);
|
|
pContext->EventQMemBlock = NULL;
|
|
}
|
|
if (pContext->StatusQMemBlock)
|
|
{
|
|
pContext->StatusQMemBlock->destroy(pContext->StatusQMemBlock);
|
|
pContext->StatusQMemBlock = NULL;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputEvtDevicePrepareHardware(
|
|
IN WDFDEVICE Device,
|
|
IN WDFCMRESLIST ResourcesRaw,
|
|
IN WDFCMRESLIST ResourcesTranslated)
|
|
{
|
|
PINPUT_DEVICE pContext = GetDeviceContext(Device);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(ResourcesRaw);
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);
|
|
|
|
status = VirtIOWdfInitialize(
|
|
&pContext->VDevice,
|
|
Device,
|
|
ResourcesTranslated,
|
|
NULL,
|
|
VIOINPUT_DRIVER_MEMORY_TAG);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "VirtIOWdfInitialize failed with %x\n", status);
|
|
return status;
|
|
}
|
|
|
|
pContext->EventQMemBlock = VirtIOWdfDeviceAllocDmaMemorySliced(
|
|
&pContext->VDevice.VIODevice, PAGE_SIZE, sizeof(VIRTIO_INPUT_EVENT));
|
|
pContext->StatusQMemBlock = VirtIOWdfDeviceAllocDmaMemorySliced(
|
|
&pContext->VDevice.VIODevice, PAGE_SIZE, sizeof(VIRTIO_INPUT_EVENT_WITH_REQUEST));
|
|
|
|
if (!pContext->EventQMemBlock || !pContext->StatusQMemBlock) {
|
|
VIOInputFreeMemBlocks(pContext);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Figure out what kind of input device this is and build a
|
|
// corresponding HID report descriptor.
|
|
status = VIOInputBuildReportDescriptor(pContext);
|
|
|
|
if (NT_SUCCESS(status) && !pContext->bChildPdoCreated)
|
|
{
|
|
// Create a child PDO with an instance path based on the
|
|
// HID report descriptor (hash). This is to make sure that
|
|
// the devnode won't be reused when a different virtio
|
|
// input device is plugged into the same PCI slot.
|
|
// viohidkmdf.sys (build by the hidpassthrough project) is
|
|
// the FDO for the child, passing IOCTL IRPs back to us.
|
|
status = VIOInputCreateChildPdo(Device);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
pContext->bChildPdoCreated = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
VIOInputFreeMemBlocks(pContext);
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS
|
|
VIOInputCreateChildPdo(
|
|
IN WDFDEVICE hDevice)
|
|
{
|
|
PINPUT_DEVICE pContext = GetDeviceContext(hDevice);
|
|
PWDFDEVICE_INIT pDeviceInit = NULL;
|
|
PPDO_EXTENSION PdoExtension;
|
|
WDFDEVICE hChild;
|
|
WDF_OBJECT_ATTRIBUTES pdoAttributes;
|
|
WDF_DEVICE_PNP_CAPABILITIES PnpCaps;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
DECLARE_CONST_UNICODE_STRING(deviceLocation, L"VIOINPUT");
|
|
DECLARE_CONST_UNICODE_STRING(deviceId, L"VIOINPUT\\REV_01");
|
|
DECLARE_UNICODE_STRING_SIZE(buffer, 32);
|
|
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__);
|
|
|
|
pDeviceInit = WdfPdoInitAllocate(hDevice);
|
|
if (pDeviceInit == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
status = WdfPdoInitAssignDeviceID(pDeviceInit, &deviceId);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
status = WdfPdoInitAddHardwareID(pDeviceInit, &deviceId);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
status = WdfPdoInitAddCompatibleID(pDeviceInit, &deviceId);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
status = RtlUnicodeStringPrintf(
|
|
&buffer,
|
|
L"%08I64x",
|
|
pContext->HidReportDescriptorHash);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
status = WdfPdoInitAssignInstanceID(pDeviceInit, &buffer);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_EXTENSION);
|
|
status = WdfDeviceCreate(
|
|
&pDeviceInit,
|
|
&pdoAttributes,
|
|
&hChild);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pDeviceInit = NULL;
|
|
|
|
// hide the child from Device Manager
|
|
WDF_DEVICE_PNP_CAPABILITIES_INIT(&PnpCaps);
|
|
PnpCaps.NoDisplayInUI = WdfTrue;
|
|
PnpCaps.UniqueID = WdfFalse;
|
|
WdfDeviceSetPnpCapabilities(hChild, &PnpCaps);
|
|
|
|
// initialize the PDO extension
|
|
PdoExtension = PdoGetExtension(hChild);
|
|
RtlZeroMemory(PdoExtension, sizeof(PDO_EXTENSION));
|
|
PdoExtension->Version = PDO_EXTENSION_VERSION;
|
|
PdoExtension->BusFdo = WdfDeviceWdmGetDeviceObject(hDevice);
|
|
|
|
// add the child
|
|
status = WdfFdoAddStaticChild(hDevice, hChild);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
WdfObjectDelete(hChild);
|
|
}
|
|
|
|
Exit:
|
|
if (pDeviceInit != NULL)
|
|
{
|
|
WdfDeviceInitFree(pDeviceInit);
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputEvtDeviceReleaseHardware(
|
|
IN WDFDEVICE Device,
|
|
IN WDFCMRESLIST ResourcesTranslated)
|
|
{
|
|
PINPUT_DEVICE pContext = GetDeviceContext(Device);
|
|
PSINGLE_LIST_ENTRY entry;
|
|
ULONG i;
|
|
|
|
UNREFERENCED_PARAMETER(ResourcesTranslated);
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);
|
|
|
|
VirtIOWdfShutdown(&pContext->VDevice);
|
|
|
|
for (i = 0; i < pContext->uNumOfClasses; i++)
|
|
{
|
|
PINPUT_CLASS_COMMON pClass = pContext->InputClasses[i];
|
|
if (pClass->CleanupFunc)
|
|
{
|
|
pClass->CleanupFunc(pClass);
|
|
}
|
|
VIOInputFree(&pClass->pHidReport);
|
|
VIOInputFree(&pClass);
|
|
}
|
|
pContext->uNumOfClasses = 0;
|
|
|
|
VIOInputFree(&pContext->HidReportDescriptor);
|
|
|
|
VIOInputFreeMemBlocks(pContext);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
VIOInputInitAllQueues(
|
|
IN WDFOBJECT Device)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PINPUT_DEVICE pContext = GetDeviceContext(Device);
|
|
|
|
struct virtqueue *vqs[2];
|
|
VIRTIO_WDF_QUEUE_PARAM params[2];
|
|
|
|
// event
|
|
params[0].Interrupt = pContext->QueuesInterrupt;
|
|
|
|
// status
|
|
params[1].Interrupt = pContext->QueuesInterrupt;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
status = VirtIOWdfInitQueues(&pContext->VDevice, 2, vqs, params);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
pContext->EventQ = vqs[0];
|
|
pContext->StatusQ = vqs[1];
|
|
}
|
|
else
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "VirtIOWdfInitQueues returned %x\n", status);
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
VIOInputShutDownAllQueues(IN WDFOBJECT WdfDevice)
|
|
{
|
|
PINPUT_DEVICE pContext = GetDeviceContext(WdfDevice);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
VirtIOWdfDestroyQueues(&pContext->VDevice);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputFillEventQueue(PINPUT_DEVICE pContext)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PVIRTIO_INPUT_EVENT buf = NULL;
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
for (;;)
|
|
{
|
|
PHYSICAL_ADDRESS pa;
|
|
buf = pContext->EventQMemBlock->get_slice(pContext->EventQMemBlock, &pa);
|
|
if (buf == NULL)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "VIRTIO_INPUT_EVENT alloc failed\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
WdfSpinLockAcquire(pContext->EventQLock);
|
|
status = VIOInputAddInBuf(pContext->EventQ, buf, pa);
|
|
WdfSpinLockRelease(pContext->EventQLock);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
pContext->EventQMemBlock->return_slice(pContext->EventQMemBlock, buf);
|
|
break;
|
|
}
|
|
}
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- %s\n", __FUNCTION__);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS
|
|
VIOInputAddBuf(
|
|
IN struct virtqueue *vq,
|
|
IN PVIRTIO_INPUT_EVENT buf,
|
|
IN PHYSICAL_ADDRESS pa,
|
|
IN BOOLEAN out)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
struct VirtIOBufferDescriptor sg;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_QUEUEING, "--> %s buf = %p, pa %I64x\n", __FUNCTION__, buf, pa.QuadPart);
|
|
if (buf == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
if (vq == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
sg.physAddr = pa;
|
|
sg.length = sizeof(VIRTIO_INPUT_EVENT);
|
|
|
|
if (0 > virtqueue_add_buf(vq, &sg, (out ? 1 : 0), (out ? 0 : 1), buf, NULL, 0))
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR, DBG_QUEUEING, "<-- %s cannot add_buf\n", __FUNCTION__);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
virtqueue_kick(vq);
|
|
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_QUEUEING, "<-- %s\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputAddInBuf(
|
|
IN struct virtqueue *vq,
|
|
IN PVIRTIO_INPUT_EVENT buf,
|
|
IN PHYSICAL_ADDRESS pa)
|
|
{
|
|
return VIOInputAddBuf(vq, buf, pa, FALSE);
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputAddOutBuf(
|
|
IN struct virtqueue *vq,
|
|
IN PVIRTIO_INPUT_EVENT buf,
|
|
IN PHYSICAL_ADDRESS pa)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_QUEUEING, "%s %p\n", __FUNCTION__, buf);
|
|
return VIOInputAddBuf(vq, buf, pa, TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputEvtDeviceD0Entry(
|
|
IN WDFDEVICE Device,
|
|
IN WDF_POWER_DEVICE_STATE PreviousState)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PINPUT_DEVICE pContext = GetDeviceContext(Device);
|
|
|
|
UNREFERENCED_PARAMETER(PreviousState);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
|
|
|
status = VIOInputInitAllQueues(Device);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
VirtIOWdfSetDriverOK(&pContext->VDevice);
|
|
VIOInputFillEventQueue(pContext);
|
|
}
|
|
else
|
|
{
|
|
VirtIOWdfSetDriverFailed(&pContext->VDevice);
|
|
}
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
VIOInputEvtDeviceD0Exit(
|
|
IN WDFDEVICE Device,
|
|
IN WDF_POWER_DEVICE_STATE TargetState)
|
|
{
|
|
PINPUT_DEVICE pContext = GetDeviceContext(Device);
|
|
PVIRTIO_INPUT_EVENT buf;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"--> %s TargetState: %d\n",
|
|
__FUNCTION__, TargetState);
|
|
|
|
PAGED_CODE();
|
|
|
|
// reset the device to make sure it's not processing the event queue anymore
|
|
virtio_device_reset(&pContext->VDevice.VIODevice);
|
|
|
|
// now with the queue stopped, free the buffers we've pushed to it
|
|
if (pContext->EventQ)
|
|
{
|
|
while (buf = (PVIRTIO_INPUT_EVENT)virtqueue_detach_unused_buf(pContext->EventQ))
|
|
{
|
|
pContext->EventQMemBlock->return_slice(pContext->EventQMemBlock, buf);
|
|
}
|
|
}
|
|
VIOInputShutDownAllQueues(Device);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|