NTSTATUS VIOSerialEvtDeviceD0EntryPostInterruptsEnabled( IN WDFDEVICE WdfDevice, IN WDF_POWER_DEVICE_STATE PreviousState ) { PPORTS_DEVICE pContext = GetPortsDevice(WdfDevice); UNREFERENCED_PARAMETER(PreviousState); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__); PAGED_CODE(); if(!pContext->DeviceOK) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Sending VIRTIO_CONSOLE_DEVICE_READY 0\n"); VIOSerialSendCtrlMsg(WdfDevice, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 0); } else { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Setting VIRTIO_CONFIG_S_DRIVER_OK flag\n"); VirtIODeviceAddStatus(pContext->pIODevice, VIRTIO_CONFIG_S_DRIVER_OK); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Sending VIRTIO_CONSOLE_DEVICE_READY 1\n"); VIOSerialSendCtrlMsg(WdfDevice, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__); return STATUS_SUCCESS; }
VOID VIOSerialCtrlWorkHandler( IN WDFDEVICE Device ) { struct virtqueue *vq; PPORT_BUFFER buf; UINT len; NTSTATUS status = STATUS_SUCCESS; PPORTS_DEVICE pContext = GetPortsDevice(Device); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> %s\n", __FUNCTION__); vq = pContext->c_ivq; ASSERT(vq); WdfSpinLockAcquire(pContext->CInVqLock); while ((buf = virtqueue_get_buf(vq, &len))) { WdfSpinLockRelease(pContext->CInVqLock); buf->len = len; buf->offset = 0; VIOSerialHandleCtrlMsg(Device, buf); WdfSpinLockAcquire(pContext->CInVqLock); status = VIOSerialAddInBuf(vq, buf); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "%s::%d Error adding buffer to queue\n", __FUNCTION__, __LINE__); VIOSerialFreeBuffer(buf); } } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- %s\n", __FUNCTION__); WdfSpinLockRelease(pContext->CInVqLock); }
NTSTATUS VIOSerialEvtDeviceD0Exit( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE TargetState ) { PPORTS_DEVICE pContext = GetPortsDevice(Device); PPORT_BUFFER buf; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"--> %s TargetState: %d\n", __FUNCTION__, TargetState); PAGED_CODE(); while (buf = (PPORT_BUFFER)virtqueue_detach_unused_buf(pContext->c_ivq)) { VIOSerialFreeBuffer(buf); } VIOSerialShutDownAllQueues(Device); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__); return STATUS_SUCCESS; }
NTSTATUS VIOSerialEvtDeviceD0Entry( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState ) { NTSTATUS status = STATUS_SUCCESS; PPORTS_DEVICE pContext = GetPortsDevice(Device); UNREFERENCED_PARAMETER(PreviousState); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__); if(!pContext->DeviceOK) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Setting VIRTIO_CONFIG_S_FAILED flag\n"); VirtIODeviceAddStatus(pContext->pIODevice, VIRTIO_CONFIG_S_FAILED); } else { status = VIOSerialInitAllQueues(Device); if (NT_SUCCESS(status) && pContext->isHostMultiport) { VIOSerialFillQueue(pContext->c_ivq, pContext->CVqLock); } } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__); return status; }
NTSTATUS VIOSerialShutDownAllQueues( IN WDFOBJECT WdfDevice, IN BOOLEAN bFinal) { NTSTATUS status = STATUS_SUCCESS; PPORTS_DEVICE pContext = GetPortsDevice(WdfDevice); UINT nr_ports, i; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__); VirtIODeviceRemoveStatus(pContext->pIODevice , VIRTIO_CONFIG_S_DRIVER_OK); if(pContext->isHostMultiport) { DeleteQueue(&pContext->c_ivq, bFinal); DeleteQueue(&pContext->c_ovq, bFinal); } nr_ports = pContext->consoleConfig.max_nr_ports; for(i = 0; i < nr_ports; i++ ) { if(pContext->in_vqs && pContext->in_vqs[i]) { DeleteQueue(&(pContext->in_vqs[i]), bFinal); } if(pContext->out_vqs && pContext->out_vqs[i]) { DeleteQueue(&(pContext->out_vqs[i]), bFinal); } } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__); return status; }
NTSTATUS VIOSerialEvtDeviceD0Entry( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState ) { PPORTS_DEVICE pContext = GetPortsDevice(Device); UNREFERENCED_PARAMETER(PreviousState); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__); if(!pContext->DeviceOK) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Setting VIRTIO_CONFIG_S_FAILED flag\n"); VirtIODeviceAddStatus(pContext->pIODevice, VIRTIO_CONFIG_S_FAILED); } else { VIOSerialInitAllQueues(Device); VIOSerialRenewAllPorts(Device); } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__); return STATUS_SUCCESS; }
static NTSTATUS VIOSerialInitInterruptHandling( IN WDFDEVICE hDevice) { WDF_OBJECT_ATTRIBUTES attributes; WDF_INTERRUPT_CONFIG interruptConfig; PPORTS_DEVICE pContext = GetPortsDevice(hDevice); NTSTATUS status = STATUS_SUCCESS; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, PORTS_DEVICE); WDF_INTERRUPT_CONFIG_INIT( &interruptConfig, VIOSerialInterruptIsr, VIOSerialInterruptDpc ); interruptConfig.EvtInterruptEnable = VIOSerialInterruptEnable; interruptConfig.EvtInterruptDisable = VIOSerialInterruptDisable; status = WdfInterruptCreate( hDevice, &interruptConfig, &attributes, &pContext->WdfInterrupt ); if (!NT_SUCCESS (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; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__); return status; }
VOID VIOSerialSendCtrlMsg( IN WDFDEVICE Device, IN ULONG id, IN USHORT event, IN USHORT value ) { struct VirtIOBufferDescriptor sg; struct virtqueue *vq; UINT len; PPORTS_DEVICE pContext = GetPortsDevice(Device); VIRTIO_CONSOLE_CONTROL cpkt; int cnt = 0; if (!pContext->isHostMultiport) { return; } vq = pContext->c_ovq; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s vq = %p\n", __FUNCTION__, vq); cpkt.id = id; cpkt.event = event; cpkt.value = value; sg.physAddr = MmGetPhysicalAddress(&cpkt); sg.ulSize = sizeof(cpkt); WdfSpinLockAcquire(pContext->CVqLock); if(0 <= vq->vq_ops->add_buf(vq, &sg, 1, 0, &cpkt, NULL, 0)) { vq->vq_ops->kick(vq); while(!vq->vq_ops->get_buf(vq, &len)) { KeStallExecutionProcessor(50); if(++cnt > RETRY_THRESHOLD) { TraceEvents(TRACE_LEVEL_FATAL, DBG_PNP, "<-> %s retries = %d\n", __FUNCTION__, cnt); break; } } } WdfSpinLockRelease(pContext->CVqLock); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s cnt = %d\n", __FUNCTION__, cnt); }
VOID VIOSerialAddPort( IN WDFDEVICE Device, IN ULONG id ) { VIOSERIAL_PORT port; PPORTS_DEVICE pContext = GetPortsDevice(Device); NTSTATUS status = STATUS_SUCCESS; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"%s DeviceId = %d :: PortId = %d\n", __FUNCTION__, pContext->DeviceId, id); WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( &port.Header, sizeof(port) ); port.PortId = id; port.DeviceId = pContext->DeviceId; port.NameString.Buffer = NULL; port.NameString.Length = 0; port.NameString.MaximumLength = 0; port.InBuf = NULL; port.HostConnected = port.GuestConnected = FALSE; port.OutVqFull = FALSE; port.Removed = FALSE; port.BusDevice = Device; status = WdfChildListAddOrUpdateChildDescriptionAsPresent( WdfFdoGetDefaultChildList(Device), &port.Header, NULL ); if (status == STATUS_OBJECT_NAME_EXISTS) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "The description is already present in the list, the serial number is not unique.\n"); return; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "WdfChildListAddOrUpdateChildDescriptionAsPresent = 0x%x.\n", status); }
void DumpQueues(WDFOBJECT Device) { ULONG i, nr_ports; PPORTS_DEVICE pContext = GetPortsDevice(Device); nr_ports = pContext->consoleConfig.max_nr_ports; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "pContext->c_ivq %p\n", pContext->c_ivq); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "pContext->c_ovq %p\n", pContext->c_ovq); for (i = 0; i < nr_ports; ++i) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "pContext->in_vqs[%d] %p\n", i, pContext->in_vqs[i]); } for (i = 0; i < nr_ports; ++i) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "pContext->out_vqs[%d] %p\n", i, pContext->out_vqs[i]); } }
static NTSTATUS VIOSerialInitAllQueues( IN WDFOBJECT Device) { NTSTATUS status = STATUS_SUCCESS; PPORTS_DEVICE pContext = GetPortsDevice(Device); UINT nr_ports, i, j; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__); nr_ports = pContext->consoleConfig.max_nr_ports; if(pContext->isHostMultiport) { nr_ports++; } for(i = 0, j = 0; i < nr_ports; i++) { if(i == 1) // Control Port { if (pContext->c_ivq) VirtIODeviceRenewQueue(pContext->c_ivq); else pContext->c_ivq = FindVirtualQueue(pContext->pIODevice, 2); if (pContext->c_ovq) VirtIODeviceRenewQueue(pContext->c_ovq); else pContext->c_ovq = FindVirtualQueue(pContext->pIODevice, 3); } else { if (pContext->in_vqs[j]) VirtIODeviceRenewQueue(pContext->in_vqs[j]); else pContext->in_vqs[j] = FindVirtualQueue(pContext->pIODevice, i * 2); if (pContext->out_vqs[j]) VirtIODeviceRenewQueue(pContext->out_vqs[j]); else pContext->out_vqs[j] = FindVirtualQueue(pContext->pIODevice, (i * 2) + 1); ++j; } } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__); return status; }
VOID VIOSerialSendCtrlMsg( IN WDFDEVICE Device, IN ULONG id, IN USHORT event, IN USHORT value ) { struct VirtIOBufferDescriptor sg; struct virtqueue *vq; UINT len; PPORTS_DEVICE pContext = GetPortsDevice(Device); VIRTIO_CONSOLE_CONTROL cpkt; if (!pContext->isHostMultiport) { return; } vq = pContext->c_ovq; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s vq = %p\n", __FUNCTION__, vq); cpkt.id = id; cpkt.event = event; cpkt.value = value; sg.physAddr = MmGetPhysicalAddress(&cpkt); sg.length = sizeof(cpkt); WdfWaitLockAcquire(pContext->COutVqLock, NULL); if(0 <= virtqueue_add_buf(vq, &sg, 1, 0, &cpkt, NULL, 0)) { virtqueue_kick(vq); while(!virtqueue_get_buf(vq, &len)) { LARGE_INTEGER interval; interval.QuadPart = -1; KeDelayExecutionThread(KernelMode, FALSE, &interval); } } WdfWaitLockRelease(pContext->COutVqLock); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__); }
NTSTATUS VIOSerialPortEvtDeviceD0Entry( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState) { PVIOSERIAL_PORT port = RawPdoSerialPortGetData(Device)->port; PPORTS_DEVICE pCtx = GetPortsDevice(port->BusDevice); NTSTATUS status; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__); PAGED_CODE(); if ((pCtx->in_vqs == NULL) || (pCtx->in_vqs[port->PortId] == NULL)) { return STATUS_NOT_FOUND; } status = VIOSerialFillQueue(GetInQueue(port), port->InBufLock); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "Error allocating input queue's buffers.\n"); return status; } VIOSerialSendCtrlMsg(port->BusDevice, port->PortId, VIRTIO_CONSOLE_PORT_READY, 1); if (port->GuestConnected) { VIOSerialSendCtrlMsg(port->BusDevice, port->PortId, VIRTIO_CONSOLE_PORT_OPEN, 1); } port->Removed = FALSE; VIOSerialEnableInterruptQueue(GetInQueue(port)); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__); return status; }
NTSTATUS VIOSerialEvtDeviceReleaseHardware( IN WDFDEVICE Device, IN WDFCMRESLIST ResourcesTranslated) { PPORTS_DEVICE pContext = GetPortsDevice(Device); UNREFERENCED_PARAMETER(ResourcesTranslated); PAGED_CODE(); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "--> %s\n", __FUNCTION__); if (pContext->pPortBase && pContext->bPortMapped) { MmUnmapIoSpace(pContext->pPortBase, pContext->uPortLength); } pContext->pPortBase = (ULONG_PTR)NULL; if(pContext->in_vqs) { ExFreePoolWithTag(pContext->in_vqs, VIOSERIAL_DRIVER_MEMORY_TAG); pContext->in_vqs = NULL; } if(pContext->out_vqs) { ExFreePoolWithTag(pContext->out_vqs, VIOSERIAL_DRIVER_MEMORY_TAG); pContext->out_vqs = NULL; } if (pContext->pIODevice) { ExFreePoolWithTag(pContext->pIODevice, VIOSERIAL_DRIVER_MEMORY_TAG); pContext->pIODevice = NULL; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__); return STATUS_SUCCESS; }
VOID VIOSerialHandleCtrlMsg( IN WDFDEVICE Device, IN PPORT_BUFFER buf ) { PPORTS_DEVICE pContext = GetPortsDevice(Device); PVIRTIO_CONSOLE_CONTROL cpkt; PVIOSERIAL_PORT port; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__); cpkt = (PVIRTIO_CONSOLE_CONTROL)((ULONG_PTR)buf->va_buf + buf->offset); port = VIOSerialFindPortById(Device, cpkt->id); if (!port && (cpkt->event != VIRTIO_CONSOLE_PORT_ADD)) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Invalid index %u in control packet\n", cpkt->id); } switch (cpkt->event) { case VIRTIO_CONSOLE_PORT_ADD: if (port) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "VIRTIO_CONSOLE_PORT_ADD id = %d\n", cpkt->id); break; } if (cpkt->id >= pContext->consoleConfig.max_nr_ports) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "Out-of-bound id %u\n", cpkt->id); break; } VIOSerialAddPort(Device, cpkt->id); break; case VIRTIO_CONSOLE_PORT_REMOVE: if (!port) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "VIRTIO_CONSOLE_PORT_REMOVE invalid id = %d\n", cpkt->id); break; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "VIRTIO_CONSOLE_PORT_REMOVE id = %d\n", cpkt->id); VIOSerialRemovePort(Device, port); break; case VIRTIO_CONSOLE_CONSOLE_PORT: TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "VIRTIO_CONSOLE_CONSOLE_PORT id = %d value = %u\n", cpkt->id, cpkt->value); if (cpkt->value) { VIOSerialInitPortConsole(Device,port); } break; case VIRTIO_CONSOLE_RESIZE: TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "VIRTIO_CONSOLE_RESIZE id = %d\n", cpkt->id); break; case VIRTIO_CONSOLE_PORT_OPEN: if (port) { BOOLEAN Connected = (BOOLEAN)cpkt->value; TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "VIRTIO_CONSOLE_PORT_OPEN id = %d, HostConnected = %d\n", cpkt->id, Connected); if (port->HostConnected != Connected) { VIOSerialPortPnpNotify(Device, port, Connected); } // Someone is listening. Trigger a check to see if we have // something waiting to be told. if (port->HostConnected) { WDF_INTERRUPT_INFO info; WDFINTERRUPT *interrupt; WDF_INTERRUPT_INFO_INIT(&info); WdfInterruptGetInfo(pContext->QueuesInterrupt, &info); // Check if MSI is enabled and notify the right interrupt. interrupt = (info.Vector == 0) ? &pContext->WdfInterrupt : &pContext->QueuesInterrupt; WdfInterruptQueueDpcForIsr(*interrupt); } } else { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "VIRTIO_CONSOLE_PORT_OPEN invalid id = %d\n", cpkt->id); } break; case VIRTIO_CONSOLE_PORT_NAME: if (port) { VIOSerialPortCreateName(Device, port, buf); } break; default: TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "%s UNKNOWN event = %d\n", __FUNCTION__, cpkt->event); } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__); }
NTSTATUS VIOSerialEvtDevicePrepareHardware( IN WDFDEVICE Device, IN WDFCMRESLIST ResourcesRaw, IN WDFCMRESLIST ResourcesTranslated) { 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; UNREFERENCED_PARAMETER(ResourcesRaw); PAGED_CODE(); 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); pContext->pIODevice = (VirtIODevice *)ExAllocatePoolWithTag( NonPagedPool, size_to_allocate, VIOSERIAL_DRIVER_MEMORY_TAG); if (NULL == pContext->pIODevice) { return STATUS_INSUFFICIENT_RESOURCES; } nListSize = WdfCmResourceListGetCount(ResourcesTranslated); for (i = 0; i < nListSize; i++) { if(pResDescriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i)) { switch(pResDescriptor->Type) { case CmResourceTypePort : pContext->bPortMapped = (pResDescriptor->Flags & CM_RESOURCE_PORT_IO) ? FALSE : TRUE; pContext->PortBasePA = pResDescriptor->u.Port.Start; pContext->uPortLength = pResDescriptor->u.Port.Length; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "IO Port Info [%08I64X-%08I64X]\n", pResDescriptor->u.Port.Start.QuadPart, pResDescriptor->u.Port.Start.QuadPart + pResDescriptor->u.Port.Length); if (pContext->bPortMapped ) { pContext->pPortBase = MmMapIoSpace(pContext->PortBasePA, pContext->uPortLength, MmNonCached); if (!pContext->pPortBase) { TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "%s>>> Failed to map IO port!\n", __FUNCTION__); return STATUS_INSUFFICIENT_RESOURCES; } } else { pContext->pPortBase = (PVOID)(ULONG_PTR)pContext->PortBasePA.QuadPart; } bPortFound = TRUE; break; case CmResourceTypeInterrupt: break; } } } if(!bPortFound) { 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); VirtIODeviceReset(pContext->pIODevice); VirtIODeviceAddStatus(pContext->pIODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE); pContext->consoleConfig.max_nr_ports = 1; if(pContext->isHostMultiport = VirtIODeviceGetHostFeature(pContext->pIODevice, VIRTIO_CONSOLE_F_MULTIPORT)) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "We have multiport host\n"); VirtIODeviceEnableGuestFeature(pContext->pIODevice, VIRTIO_CONSOLE_F_MULTIPORT); VirtIODeviceGet(pContext->pIODevice, FIELD_OFFSET(CONSOLE_CONFIG, max_nr_ports), &pContext->consoleConfig.max_nr_ports, sizeof(pContext->consoleConfig.max_nr_ports)); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "VirtIOConsoleConfig->max_nr_ports %d\n", pContext->consoleConfig.max_nr_ports); if (pContext->consoleConfig.max_nr_ports > pContext->pIODevice->maxQueues / 2) { pContext->consoleConfig.max_nr_ports = pContext->pIODevice->maxQueues / 2; TraceEvents(TRACE_LEVEL_WARNING, DBG_PNP, "VirtIOConsoleConfig->max_nr_ports limited to %d\n", pContext->consoleConfig.max_nr_ports); } } if(pContext->isHostMultiport) { WDF_OBJECT_ATTRIBUTES attributes; WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = Device; status = WdfSpinLockCreate( &attributes, &pContext->CVqLock ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfSpinLockCreate failed 0x%x\n", status); return status; } } else { //FIXME // VIOSerialAddPort(Device, 0); } nr_ports = pContext->consoleConfig.max_nr_ports; pContext->in_vqs = (struct virtqueue**)ExAllocatePoolWithTag( NonPagedPool, nr_ports * sizeof(struct virtqueue*), VIOSERIAL_DRIVER_MEMORY_TAG); if(pContext->in_vqs == NULL) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT,"ExAllocatePoolWithTag failed\n"); return STATUS_INSUFFICIENT_RESOURCES; } memset(pContext->in_vqs, 0, nr_ports * sizeof(struct virtqueue*)); pContext->out_vqs = (struct virtqueue**)ExAllocatePoolWithTag( NonPagedPool, nr_ports * sizeof(struct virtqueue*), VIOSERIAL_DRIVER_MEMORY_TAG ); if(pContext->out_vqs == NULL) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "ExAllocatePoolWithTag failed\n"); return STATUS_INSUFFICIENT_RESOURCES; } memset(pContext->out_vqs, 0, nr_ports * sizeof(struct virtqueue*)); pContext->DeviceOK = TRUE; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__); return status; }
VOID VIOSerialRenewAllPorts( IN WDFDEVICE Device ) { NTSTATUS status = STATUS_SUCCESS; WDFCHILDLIST list; WDF_CHILD_LIST_ITERATOR iterator; PPORTS_DEVICE pContext = GetPortsDevice(Device); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"--> %s\n", __FUNCTION__); if(pContext->isHostMultiport) { VIOSerialFillQueue(pContext->c_ivq, pContext->CVqLock); } list = WdfFdoGetDefaultChildList(Device); WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren ); WdfChildListBeginIteration(list, &iterator); for (;;) { WDF_CHILD_RETRIEVE_INFO childInfo; VIOSERIAL_PORT vport; WDFDEVICE hChild; WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( &vport.Header, sizeof(vport) ); WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &vport.Header); status = WdfChildListRetrieveNextDevice( list, &iterator, &hChild, &childInfo ); if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES) { break; } ASSERT(childInfo.Status == WdfChildListRetrieveDeviceSuccess); VIOSerialEnableInterruptQueue(GetInQueue(&vport)); WdfIoQueueStart(vport.ReadQueue); WdfIoQueueStart(vport.WriteQueue); WdfIoQueueStart(vport.IoctlQueue); if(vport.GuestConnected) { VIOSerialSendCtrlMsg(vport.BusDevice, vport.PortId, VIRTIO_CONSOLE_PORT_OPEN, 1); } } WdfChildListEndIteration(list, &iterator); WdfChildListUpdateAllChildDescriptionsAsPresent(list); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"<-- %s\n", __FUNCTION__); return; }
NTSTATUS VIOSerialEvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit) { NTSTATUS status = STATUS_SUCCESS; WDF_OBJECT_ATTRIBUTES Attributes; WDFDEVICE hDevice; WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; WDF_CHILD_LIST_CONFIG ChildListConfig; PNP_BUS_INFORMATION busInfo; PPORTS_DEVICE pContext = NULL; UNREFERENCED_PARAMETER(Driver); PAGED_CODE(); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__); WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); PnpPowerCallbacks.EvtDevicePrepareHardware = VIOSerialEvtDevicePrepareHardware; PnpPowerCallbacks.EvtDeviceReleaseHardware = VIOSerialEvtDeviceReleaseHardware; PnpPowerCallbacks.EvtDeviceD0Entry = VIOSerialEvtDeviceD0Entry; PnpPowerCallbacks.EvtDeviceD0Exit = VIOSerialEvtDeviceD0Exit; PnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = VIOSerialEvtDeviceD0EntryPostInterruptsEnabled; WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks); WDF_CHILD_LIST_CONFIG_INIT( &ChildListConfig, sizeof(VIOSERIAL_PORT), VIOSerialDeviceListCreatePdo ); ChildListConfig.EvtChildListIdentificationDescriptionDuplicate = VIOSerialEvtChildListIdentificationDescriptionDuplicate; ChildListConfig.EvtChildListIdentificationDescriptionCompare = VIOSerialEvtChildListIdentificationDescriptionCompare; ChildListConfig.EvtChildListIdentificationDescriptionCleanup = VIOSerialEvtChildListIdentificationDescriptionCleanup; WdfFdoInitSetDefaultChildListConfig( DeviceInit, &ChildListConfig, WDF_NO_OBJECT_ATTRIBUTES ); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, PORTS_DEVICE); Attributes.SynchronizationScope = WdfSynchronizationScopeDevice; Attributes.ExecutionLevel = WdfExecutionLevelPassive; status = WdfDeviceCreate(&DeviceInit, &Attributes, &hDevice); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceCreate failed - 0x%x\n", status); return status; } status = VIOSerialInitInterruptHandling(hDevice); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "VIOSerialInitInterruptHandling failed - 0x%x\n", status); } status = WdfDeviceCreateDeviceInterface( hDevice, &GUID_VIOSERIAL_CONTROLLER, NULL ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "WdfDeviceCreateDeviceInterface failed - 0x%x\n", status); return status; } pContext = GetPortsDevice(hDevice); pContext->DeviceId = gDeviceCount++; busInfo.BusTypeGuid = GUID_DEVCLASS_PORT_DEVICE; busInfo.LegacyBusType = PNPBus; busInfo.BusNumber = pContext->DeviceId; WdfDeviceSetBusInformationForChildren(hDevice, &busInfo); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, "<-- %s\n", __FUNCTION__); return status; }
static NTSTATUS VIOSerialInitAllQueues( IN WDFOBJECT Device) { 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) { nr_ports++; } for(i = 0, j = 0; i < nr_ports; i++) { if(i == 1) // Control Port { if (pContext->c_ivq) VirtIODeviceRenewQueue(pContext->c_ivq); else pContext->c_ivq = FindVirtualQueue(pContext->pIODevice, 2, ControlVector); if (pContext->c_ovq) VirtIODeviceRenewQueue(pContext->c_ovq); else 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, QueuesVector); if (pContext->out_vqs[j]) VirtIODeviceRenewQueue(pContext->out_vqs[j]); else pContext->out_vqs[j] = FindVirtualQueue(pContext->pIODevice, (i * 2) + 1, QueuesVector); ++j; } } if (pContext->isHostMultiport && (pContext->c_ovq == NULL)) { status = STATUS_NOT_FOUND; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s\n", __FUNCTION__); return status; }