Help Needed with Virtual Audio Driver Development using SimpleAudioSample

Hello All,

I'm developing a virtual audio driver using the example from this GitHub repository.

I want to get the data from a user application and feed the audio data to the virtual driver for the capturing pipeline. I have created an IOCTL and can send audio data from the user application to the driver, which seems to be working fine as I verified by dumping it into a file.

However, I have two problems now:

  1. After creating the IOCTL and handling the major IRP create and IRP close, I'm not getting the endpoints. I have attached my major function handling the IRP below.
  2. How do I send audio data from adapter.cpp where I'm receiving the data to the CMiniportWaveRTStream class? I know I need to pass the data to CMiniportWaveRTStream::WriteBytes to write data to the mic.

In DriverEntry, I have created the IOCTL (IoCreateDevice) and symbolic link (IoCreateSymbolicLink). Here is my code example:

if (!NT_SUCCESS(IoCreateDevice(DriverObject, 0, &DEVICE_NAME, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DriverObject->DeviceObject))) {
    DPF_ENTER(("Siva : IO Create device failed"));
    ntStatus = STATUS_UNSUCCESSFUL;
    goto Done;
}

// Routines that will execute once a handle to our device's symbolic link is opened/closed
DriverObject->MajorFunction[IRP_MJ_CREATE] = MajorFunctions;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MajorFunctions;
// Routine for handling IO requests from userland
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SimpleAudioSampleDeviceControl;

DPF_ENTER(("Siva Create symbolic link"));
if (NT_SUCCESS(IoCreateSymbolicLink(&DEVICE_SYMBOLIC_NAME, &DEVICE_NAME))) {
    DPF_ENTER(("Siva : Symbolic link created"));
} else {
    DPF_ENTER(("Siva : Symbolic link failed"));
    IoDeleteDevice(DriverObject->DeviceObject);
    ntStatus = STATUS_UNSUCCESSFUL;
    goto Done;
}

NTSTATUS MajorFunctions(PDEVICE_OBJECT DriverObject, PIRP Irp) {
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;

    UNREFERENCED_PARAMETER(DriverObject);
    PAGED_CODE();
    PIO_STACK_LOCATION stackLocation = IoGetCurrentIrpStackLocation(Irp);

    switch (stackLocation->MajorFunction) {
    case IRP_MJ_CREATE:
        DbgPrint("siva: IRP_MJ_CREATE File Name: %wZ\n", stackLocation->FileObject->FileName);
        break;
    case IRP_MJ_CLOSE:
        DbgPrint("siva: IRP_MJ_CLOSE File Name: %wZ\n", stackLocation->FileObject->FileName);
        break;
    default:
        DbgPrint("siva: default File Name: %wZ\n", stackLocation->FileObject->FileName);
        ntStatus = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    ntStatus = PcDispatchIrp(DriverObject, Irp);
    if (ntStatus != STATUS_SUCCESS) {
        DbgPrint("PcDispatchIrp inside if: \n");
        Irp->IoStatus.Information = 0;
        Irp->IoStatus.Status = STATUS_SUCCESS;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
    }
    return STATUS_SUCCESS;
}

Please guide me on how to move forward. Thanks in advance!

This belongs in the NTDEV category, not WINDBG. Perhaps the moderators can move it.

You have to pass the buffer down through the class inheritance tree. The adapter creates a CAdapterCommon object. That's where I store the circular buffers. Your CMiniportWaveCyclic derivative gets a copy of the CAdapterCommon during its Init handler using QueryInterface. You can define a method in CAdapterCommon that returns a pointer to the appropriate circular buffer.

Port class owns the first part of your device extension. Your section begins PORT_CLASS_DEVICE_EXTENSION_SIZE bytes in. You have to ask for the extra space when you call PcAddAdapterDevice in your AddDevice handler.

Hi @Tim_Roberts , thanks for your response! I have added the port class extension, and the PcAddAdapterDevice status is successful. However, I'm still getting a null extension when accessing it in the IOCTL handler. Here is the PcAddAdapterDevice call:

PcAddAdapterDevice(
    DriverObject,
    PhysicalDeviceObject,
    PCPFNSTARTDEVICE(StartDevice),
    maxObjects,
    PORT_CLASS_DEVICE_EXTENSION_SIZE
);

Could you help me figure out why this is happening?"

You're not asking for any extra space here. You need to add the size of your context to PORT_CLASS_DEVICE_EXTENSION_SIZE to get some space for yourself.

There should always be an extension. Are you creating your own device object for receiving these ioctls, or are you using the one Port Class creates?

@Tim_Roberts
I'm using the Port Class device object, which has an m_pCommon pointer sufficient for accessing the common class. However, the extension pointer is coming up as null.

  1. How to pass the data received from an IOCTL call to the common adapter class where I have a circular buffer:
IoCreateDevice(
    DriverObject,
    0,
    &DEVICE_NAME,
    FILE_DEVICE_UNKNOWN,
    FILE_DEVICE_SECURE_OPEN,
    FALSE,
    &DriverObject->DeviceObject
);
  1. How to get the proper extension in an IOCTL call:
ext = static_cast<PortClassDeviceContext*>(DeviceObject->DeviceExtension);

My aim is to receive data in the adapter.cpp class within an IRP_MJ_DEVICE_CONTROL handler and then pass this data to the CAdapterCommon class, where I have a circular buffer to handle the data. I'm new to driver development, so could you provide a step-by-step guide on what I need to do next? If there are any examples available, please point them out.

YOU don't call IoCreateDevice at all in an audio driver. Port Class does that, when you call PcAddAdapterDevice during your AddDevice callback.

The canonical sample is the SImpleAudioSample, but it doesn't allocate any extra device extension space.

The call you have above is asking for 0 bytes of device extension, but you won't need that whole thing.