mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-11-07 02:06:32 +00:00
[virtio-serial] Add a support for message-signaled interrupts.
This commit is contained in:
1 parent
ebe2f4715a
commit
8fe2811b21
4 files changed
+177
-78
No files matched your search
+60
-13
@@ -70,7 +70,22 @@ VIOSerialInitInterruptHandling(
|
||||
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "WdfInterruptCreate failed: %x\n", status);
|
||||
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS,
|
||||
"Failed to create control queue interrupt: %x\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
|
||||
VIOSerialInterruptIsr, VIOSerialQueuesInterruptDpc);
|
||||
|
||||
status = WdfInterruptCreate(hDevice, &interruptConfig, &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;
|
||||
}
|
||||
|
||||
@@ -174,17 +189,18 @@ VIOSerialEvtDevicePrepareHardware(
|
||||
{
|
||||
int nListSize = 0;
|
||||
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDescriptor;
|
||||
WDF_INTERRUPT_INFO interruptInfo;
|
||||
int i = 0;
|
||||
PPORTS_DEVICE pContext = GetPortsDevice(Device);
|
||||
bool bPortFound = FALSE;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
UINT nr_ports, max_queues, size_to_allocate;
|
||||
BOOLEAN MessageSignaled = FALSE;
|
||||
USHORT Interrupts = 0;
|
||||
|
||||
UNREFERENCED_PARAMETER(ResourcesRaw);
|
||||
PAGED_CODE();
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<--> %s\n", __FUNCTION__);
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__);
|
||||
|
||||
max_queues = 64; // 2 for each of max 32 ports
|
||||
size_to_allocate = VirtIODeviceSizeRequired((USHORT)max_queues);
|
||||
@@ -235,6 +251,13 @@ VIOSerialEvtDevicePrepareHardware(
|
||||
|
||||
break;
|
||||
case CmResourceTypeInterrupt:
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
|
||||
"Interrupt Level: 0x%08x, Vector: 0x%08x\n",
|
||||
pResDescriptor->u.Interrupt.Level,
|
||||
pResDescriptor->u.Interrupt.Vector);
|
||||
Interrupts += 1;
|
||||
MessageSignaled = !!(pResDescriptor->Flags &
|
||||
(CM_RESOURCE_INTERRUPT_LATCHED | CM_RESOURCE_INTERRUPT_MESSAGE));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -245,16 +268,23 @@ VIOSerialEvtDevicePrepareHardware(
|
||||
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "%s>>> %s", __FUNCTION__, "IO port wasn't found!\n");
|
||||
return STATUS_DEVICE_CONFIGURATION_ERROR;
|
||||
}
|
||||
WDF_INTERRUPT_INFO_INIT(&interruptInfo);
|
||||
WdfInterruptGetInfo(pContext->WdfInterrupt, &interruptInfo);
|
||||
|
||||
VirtIODeviceInitialize(pContext->pIODevice, (ULONG_PTR)pContext->pPortBase, size_to_allocate);
|
||||
VirtIODeviceSetMSIXUsed(pContext->pIODevice, interruptInfo.MessageSignaled);
|
||||
|
||||
VirtIODeviceSetMSIXUsed(pContext->pIODevice, MessageSignaled);
|
||||
VirtIODeviceReset(pContext->pIODevice);
|
||||
|
||||
VirtIODeviceAddStatus(pContext->pIODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE);
|
||||
|
||||
if (MessageSignaled)
|
||||
{
|
||||
WriteVirtIODeviceWord(
|
||||
pContext->pIODevice->addr + VIRTIO_MSI_CONFIG_VECTOR, Interrupts);
|
||||
Interrupts = ReadVirtIODeviceWord(
|
||||
pContext->pIODevice->addr + VIRTIO_MSI_CONFIG_VECTOR);
|
||||
}
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
|
||||
"MessageSignaled: %d Interrupts: %x.\n",
|
||||
MessageSignaled, Interrupts);
|
||||
|
||||
pContext->consoleConfig.max_nr_ports = 1;
|
||||
if(pContext->isHostMultiport = VirtIODeviceGetHostFeature(pContext->pIODevice, VIRTIO_CONSOLE_F_MULTIPORT))
|
||||
{
|
||||
@@ -366,7 +396,7 @@ VIOSerialEvtDeviceReleaseHardware(
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static struct virtqueue * FindVirtualQueue(VirtIODevice *dev, ULONG index)
|
||||
static struct virtqueue * FindVirtualQueue(VirtIODevice *dev, ULONG index, USHORT vector)
|
||||
{
|
||||
struct virtqueue *pq = NULL;
|
||||
PVOID p;
|
||||
@@ -380,6 +410,12 @@ static struct virtqueue * FindVirtualQueue(VirtIODevice *dev, ULONG index)
|
||||
if (p)
|
||||
{
|
||||
pq = VirtIODevicePrepareQueue(dev, index, MmGetPhysicalAddress(p), p, allocSize, p, FALSE);
|
||||
|
||||
if (vector != VIRTIO_MSI_NO_VECTOR)
|
||||
{
|
||||
WriteVirtIODeviceWord(dev->addr + VIRTIO_MSI_QUEUE_VECTOR, vector);
|
||||
vector = ReadVirtIODeviceWord(dev->addr + VIRTIO_MSI_QUEUE_VECTOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pq;
|
||||
@@ -413,9 +449,20 @@ VIOSerialInitAllQueues(
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
PPORTS_DEVICE pContext = GetPortsDevice(Device);
|
||||
UINT nr_ports, i, j;
|
||||
USHORT ControlVector, QueuesVector;
|
||||
WDF_INTERRUPT_INFO info;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__);
|
||||
|
||||
WDF_INTERRUPT_INFO_INIT(&info);
|
||||
WdfInterruptGetInfo(pContext->WdfInterrupt, &info);
|
||||
ControlVector = info.MessageSignaled ? 0 : VIRTIO_MSI_NO_VECTOR;
|
||||
|
||||
WDF_INTERRUPT_INFO_INIT(&info);
|
||||
WdfInterruptGetInfo(pContext->QueuesInterrupt, &info);
|
||||
QueuesVector = (ControlVector != VIRTIO_MSI_NO_VECTOR) ?
|
||||
(info.Vector ? 1 : 0) : VIRTIO_MSI_NO_VECTOR;
|
||||
|
||||
nr_ports = pContext->consoleConfig.max_nr_ports;
|
||||
if(pContext->isHostMultiport)
|
||||
{
|
||||
@@ -428,22 +475,22 @@ VIOSerialInitAllQueues(
|
||||
if (pContext->c_ivq)
|
||||
VirtIODeviceRenewQueue(pContext->c_ivq);
|
||||
else
|
||||
pContext->c_ivq = FindVirtualQueue(pContext->pIODevice, 2);
|
||||
pContext->c_ivq = FindVirtualQueue(pContext->pIODevice, 2, ControlVector);
|
||||
if (pContext->c_ovq)
|
||||
VirtIODeviceRenewQueue(pContext->c_ovq);
|
||||
else
|
||||
pContext->c_ovq = FindVirtualQueue(pContext->pIODevice, 3);
|
||||
pContext->c_ovq = FindVirtualQueue(pContext->pIODevice, 3, ControlVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pContext->in_vqs[j])
|
||||
VirtIODeviceRenewQueue(pContext->in_vqs[j]);
|
||||
else
|
||||
pContext->in_vqs[j] = FindVirtualQueue(pContext->pIODevice, i * 2);
|
||||
pContext->in_vqs[j] = FindVirtualQueue(pContext->pIODevice, i * 2, QueuesVector);
|
||||
if (pContext->out_vqs[j])
|
||||
VirtIODeviceRenewQueue(pContext->out_vqs[j]);
|
||||
else
|
||||
pContext->out_vqs[j] = FindVirtualQueue(pContext->pIODevice, (i * 2) + 1);
|
||||
pContext->out_vqs[j] = FindVirtualQueue(pContext->pIODevice, (i * 2) + 1, QueuesVector);
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
+108
-64
@@ -10,20 +10,27 @@ VIOSerialInterruptIsr(
|
||||
IN WDFINTERRUPT Interrupt,
|
||||
IN ULONG MessageID)
|
||||
{
|
||||
ULONG ret;
|
||||
PPORTS_DEVICE pContext = GetPortsDevice(WdfInterruptGetDevice(Interrupt));
|
||||
|
||||
UNREFERENCED_PARAMETER(MessageID);
|
||||
PPORTS_DEVICE pContext = GetPortsDevice(WdfInterruptGetDevice(Interrupt));
|
||||
BOOLEAN serviced;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "--> %s\n", __FUNCTION__);
|
||||
if((ret = VirtIODeviceISR(pContext->pIODevice)) > 0)
|
||||
|
||||
// Schedule a DPC if the device is using message-signaled interrupts, or
|
||||
// if the device ISR status is enabled.
|
||||
if (MessageID || VirtIODeviceISR(pContext->pIODevice))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, "Got ISR - it is ours %d!\n", ret);
|
||||
WdfInterruptQueueDpcForIsr(Interrupt);
|
||||
return TRUE;
|
||||
serviced = TRUE;
|
||||
}
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "WRONG ISR!\n");
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
serviced = FALSE;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,
|
||||
"<-- %s Serviced: %d\n", __FUNCTION__, serviced);
|
||||
|
||||
return serviced;
|
||||
}
|
||||
|
||||
VOID
|
||||
@@ -31,73 +38,110 @@ VIOSerialInterruptDpc(
|
||||
IN WDFINTERRUPT Interrupt,
|
||||
IN WDFOBJECT AssociatedObject)
|
||||
{
|
||||
UINT i;
|
||||
PPORTS_DEVICE pContext;
|
||||
PVIOSERIAL_PORT port;
|
||||
WDFDEVICE Device;
|
||||
WDFDEVICE Device = WdfInterruptGetDevice(Interrupt);
|
||||
PPORTS_DEVICE Context = GetPortsDevice(Device);
|
||||
WDF_INTERRUPT_INFO info;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "--> %s\n", __FUNCTION__);
|
||||
|
||||
ULONG information;
|
||||
NTSTATUS status;
|
||||
PUCHAR systemBuffer;
|
||||
size_t Length;
|
||||
WDFREQUEST request;
|
||||
VIOSerialCtrlWorkHandler(Device);
|
||||
|
||||
WDF_INTERRUPT_INFO_INIT(&info);
|
||||
WdfInterruptGetInfo(Context->QueuesInterrupt, &info);
|
||||
|
||||
// Using the queues' DPC if only one interrupt is availble.
|
||||
if (info.Vector == 0)
|
||||
{
|
||||
VIOSerialQueuesInterruptDpc(Interrupt, AssociatedObject);
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
VOID VIOSerialQueuesInterruptDpc(IN WDFINTERRUPT Interrupt,
|
||||
IN WDFOBJECT AssociatedObject)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFCHILDLIST PortList;
|
||||
WDFDEVICE Device;
|
||||
WDF_CHILD_LIST_ITERATOR iterator;
|
||||
WDF_CHILD_RETRIEVE_INFO PortInfo;
|
||||
VIOSERIAL_PORT VirtPort;
|
||||
VIOSERIAL_PORT *Port;
|
||||
|
||||
UNREFERENCED_PARAMETER(AssociatedObject);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "--> %s\n", __FUNCTION__);
|
||||
|
||||
if(!Interrupt)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC, "Got NULL interrupt object DPC!\n");
|
||||
return;
|
||||
}
|
||||
Device = WdfInterruptGetDevice(Interrupt);
|
||||
pContext = GetPortsDevice(Device);
|
||||
PortList = WdfFdoGetDefaultChildList(WdfInterruptGetDevice(Interrupt));
|
||||
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
|
||||
|
||||
VIOSerialCtrlWorkHandler(Device);
|
||||
for (i = 0; i < pContext->consoleConfig.max_nr_ports; ++i)
|
||||
WdfChildListBeginIteration(PortList, &iterator);
|
||||
for (;;)
|
||||
{
|
||||
port = VIOSerialFindPortById(Device, i);
|
||||
if (port)
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&PortInfo, &VirtPort.Header);
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&VirtPort.Header,
|
||||
sizeof(VirtPort));
|
||||
|
||||
status = WdfChildListRetrieveNextDevice(PortList, &iterator, &Device,
|
||||
&PortInfo);
|
||||
|
||||
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
|
||||
{
|
||||
WdfSpinLockAcquire(port->InBufLock);
|
||||
if (!port->InBuf)
|
||||
{
|
||||
port->InBuf = VIOSerialGetInBuf(port);
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "%s::%d port->InBuf = %p\n", __FUNCTION__, __LINE__, port->InBuf);
|
||||
}
|
||||
if (!port->GuestConnected)
|
||||
{
|
||||
VIOSerialDiscardPortDataLocked(port);
|
||||
}
|
||||
|
||||
if (port->InBuf)
|
||||
{
|
||||
if (port->PendingReadRequest)
|
||||
{
|
||||
request = port->PendingReadRequest;
|
||||
status = WdfRequestUnmarkCancelable(request);
|
||||
if (status != STATUS_CANCELLED)
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,"Got available read request\n");
|
||||
status = WdfRequestRetrieveOutputBuffer(request, 0, &systemBuffer, &Length);
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
port->PendingReadRequest = NULL;
|
||||
information = (ULONG)VIOSerialFillReadBufLocked(port, systemBuffer, Length);
|
||||
WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, information);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "Request = %p was cancelled\n", request);
|
||||
}
|
||||
}
|
||||
}
|
||||
WdfSpinLockRelease(port->InBufLock);
|
||||
break;
|
||||
}
|
||||
|
||||
Port = RawPdoSerialPortGetData(Device)->port;
|
||||
|
||||
WdfSpinLockAcquire(Port->InBufLock);
|
||||
if (!Port->InBuf)
|
||||
{
|
||||
Port->InBuf = (PPORT_BUFFER)VIOSerialGetInBuf(Port);
|
||||
}
|
||||
|
||||
if (!Port->GuestConnected)
|
||||
{
|
||||
VIOSerialDiscardPortDataLocked(Port);
|
||||
}
|
||||
|
||||
if (Port->InBuf && Port->PendingReadRequest)
|
||||
{
|
||||
WDFREQUEST Request;
|
||||
|
||||
Request = Port->PendingReadRequest;
|
||||
status = WdfRequestUnmarkCancelable(Request);
|
||||
if (status != STATUS_CANCELLED)
|
||||
{
|
||||
PVOID Buffer;
|
||||
size_t Length;
|
||||
|
||||
status = WdfRequestRetrieveOutputBuffer(Request, 0, &Buffer, &Length);
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
ULONG Read;
|
||||
|
||||
Port->PendingReadRequest = NULL;
|
||||
Read = (ULONG)VIOSerialFillReadBufLocked(Port, Buffer, Length);
|
||||
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS,
|
||||
Read);
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC,
|
||||
"Failed to retrieve output buffer (Status: %x Request: %p).\n",
|
||||
status, Request);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
|
||||
"Request %p was cancelled.\n", Request);
|
||||
}
|
||||
}
|
||||
WdfSpinLockRelease(Port->InBufLock);
|
||||
}
|
||||
WdfChildListEndIteration(PortList, &iterator);
|
||||
|
||||
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ EVT_WDF_DRIVER_DEVICE_ADD VIOSerialEvtDeviceAdd;
|
||||
|
||||
EVT_WDF_INTERRUPT_ISR VIOSerialInterruptIsr;
|
||||
EVT_WDF_INTERRUPT_DPC VIOSerialInterruptDpc;
|
||||
EVT_WDF_INTERRUPT_DPC VIOSerialQueuesInterruptDpc;
|
||||
EVT_WDF_INTERRUPT_ENABLE VIOSerialInterruptEnable;
|
||||
EVT_WDF_INTERRUPT_DISABLE VIOSerialInterruptDisable;
|
||||
|
||||
@@ -82,6 +83,7 @@ typedef struct _tagPortDevice
|
||||
bool bPortMapped;
|
||||
|
||||
WDFINTERRUPT WdfInterrupt;
|
||||
WDFINTERRUPT QueuesInterrupt;
|
||||
|
||||
int isHostMultiport;
|
||||
|
||||
|
||||
@@ -46,8 +46,14 @@ vioser.sys = 1,,
|
||||
[VirtioSerial_Device.NT]
|
||||
CopyFiles=Drivers_Dir
|
||||
|
||||
[VirtioPort_Device.NT]
|
||||
[VirtioSerial_Device.NT.HW]
|
||||
AddReg=VirtioSerial_AddReg
|
||||
|
||||
[VirtioSerial_AddReg]
|
||||
HKR,Interrupt Management,,0x00000010
|
||||
HKR,Interrupt Management\MessageSignaledInterruptProperties,,0x00000010
|
||||
HKR,Interrupt Management\MessageSignaledInterruptProperties,MSISupported,0x00010001,1
|
||||
HKR,Interrupt Management\MessageSignaledInterruptProperties,MessageNumberLimit,0x00010001,2
|
||||
|
||||
[Drivers_Dir]
|
||||
vioser.sys
|
||||
|
||||
Reference in new issue
Block a user