Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

Troubles creating virtual input device

hpgruberhpgruber Member Posts: 8

Hello community,

 

I am developing a small application that sends audio data (PCM) from a device to a desktop computer. On this desktop computer, the data gets pushed into a virtual microphone, which users can select in applications (Skype, Zoom, etc.). This whole process was trivial on Linux, where it took one single command line to create a virtual input device which is backed by a file that I can write to. I already expected it to be more complicated on Windows, but I underestimated just how complex this task was going to become.

 

I picked up a book to learn about Kernel Programming, and managed to write a simple WDM kernel driver that can communicate with a client application using IOCTLS. Then I checked out Microsoft's "simpleaudiosample", which I understand was split off of SYSVAD to provide a minimalistic sample. It creates a virtual speaker, which plays a sine wave. After some research, I realized that the function that is actually doing to the transfer to the audio engine is CMiniportWaveRTStream::WriteBytes.

 

So my gameplan was basically this:
 

1) I set a hook to either IRP_MJ_WRITE or IRP_MJ_DEVICE_CONTROL to receive data from the client app. For simplicity we will set the DO_BUFFERED_IO flag.

2) Since I can't write to the m_pDmaBuffer directly, i need my own private buffer that acts as a "glue". To access this buffer both from my iOCTL hook and the Miniport, I extend the device context by providing the overall context size to the PcAddAdapterDevice function.

3) In my IOCTL hook function, I read the data from the client and save it to my private buffer.

4) I set up the WriteBytes function so that it pulls data out of my private buffer and shoves it into the m_pDmaBuffer. As far as I understood, the audio engine will call this function.

 

But unfortunately, I don't feel that I'm on the right track at all. The simpleaudiosample driver is a WDF driver, and setting up those hooks seems to be the WDM way. In fact, just adding the hook to IRP_MJ_DEVICE_CONTROL crashes the OS at install. My first guess was that it is due to the WdfDriverInitNoDispatchOverride flag that is being set, but removing this leads to the same results, and honestly, at this point I am just fooling around and don't know what I'm doing. Also I can't debug the machine because I don't have access to a native Windows machine, so I use two VMs, and there is a bug in Virtualbox which makes it borderline impossible to debug (takes hours to break).

 

Do I need to use WDF queues for communicating? For example, setting 'ioQueueConfig.EvtIoWrite = myIoWrite'? But where do initiate the queue? All the samples I looked at have very simple and straightfoward structures, but the audio sample abstracts most things away to. I've spent 100+ hours on this and still feel overwhelmed by the complexity. Do I really need 9000 LOC to create a sine wave? I didn't expect a one liner as in Linux, but I can change thread priorities from a client app in 100 LOC, so the relationship seems a bit odd.

 

 

Anyway, I attached the driver entry of simpleaudiosample, the github link is here in case anyone is interested.

 

#pragma code_seg("INIT")
extern "C" DRIVER_INITIALIZE DriverEntry;
extern "C" NTSTATUS
DriverEntry
( 
    _In_  PDRIVER_OBJECT          DriverObject,
    _In_  PUNICODE_STRING         RegistryPathName
)
{
/*++

Routine Description:

  Installable driver initialization entry point.
  This entry point is called directly by the I/O system.

  All audio adapter drivers can use this code without change.

Arguments:

  DriverObject - pointer to the driver object

  RegistryPath - pointer to a unicode string representing the path,
                   to driver-specific key in the registry.

Return Value:

  STATUS_SUCCESS if successful,
  STATUS_UNSUCCESSFUL otherwise.

--*/
    NTSTATUS                    ntStatus;
    WDF_DRIVER_CONFIG           config;

    DPF(D_TERSE, ("[DriverEntry]"));

    // Copy registry Path name in a global variable to be used by modules inside driver.
    // !! NOTE !! Inside this function we are initializing the registrypath, so we MUST NOT add any failing calls
    // before the following call.
    ntStatus = CopyRegistrySettingsPath(RegistryPathName);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Registry path copy error 0x%x", ntStatus)),
        Done);

    //
    // Get registry configuration.
    //
    ntStatus = GetRegistrySettings(RegistryPathName);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("Registry Configuration error 0x%x", ntStatus)),
        Done);

    WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
    //
    // Set WdfDriverInitNoDispatchOverride flag to tell the framework
    // not to provide dispatch routines for the driver. In other words,
    // the framework must not intercept IRPs that the I/O manager has
    // directed to the driver. In this case, they will be handled by Audio
    // port driver.
    //
    config.DriverInitFlags |= WdfDriverInitNoDispatchOverride;
    config.DriverPoolTag    = MINADAPTER_POOLTAG;

    ntStatus = WdfDriverCreate(DriverObject,
                               RegistryPathName,
                               WDF_NO_OBJECT_ATTRIBUTES,
                               &config,
                               WDF_NO_HANDLE);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("WdfDriverCreate failed, 0x%x", ntStatus)),
        Done);

    //
    // Tell the class driver to initialize the driver.
    //
    ntStatus =  PcInitializeAdapterDriver(DriverObject,
                                          RegistryPathName,
                                          (PDRIVER_ADD_DEVICE)AddDevice);
    IF_FAILED_ACTION_JUMP(
        ntStatus,
        DPF(D_ERROR, ("PcInitializeAdapterDriver failed, 0x%x", ntStatus)),
        Done);

    //
    // To intercept stop/remove/surprise-remove.
    //
    DriverObject->MajorFunction[IRP_MJ_PNP] = PnpHandler;
    // uncommenting the following line crashes everything
    // DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDeviceControl;

    //
    // Hook the port class unload function
    //
    gPCDriverUnloadRoutine = DriverObject->DriverUnload;
    DriverObject->DriverUnload = DriverUnload;

    //
    // All done.
    //
    ntStatus = STATUS_SUCCESS;

Done:

    if (!NT_SUCCESS(ntStatus))
    {
        if (WdfGetDriver() != NULL)
        {
            WdfDriverMiniportUnload(WdfGetDriver());
        }

        ReleaseRegistryStringBuffer();
    }

    return ntStatus;
} // DriverEntry


NTSTATUS MyDeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    DbgPrint("I was here");
    irp->IoStatus.Status = STATUS_SUCCESS;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

Comments

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,939

    It's absolutely true that virtual audio drivers in Windows are a pain in the butt. I get big bucks for creating those.

    The key thing you're missing is that ALL communication with kernel streaming drivers goes on via ioctls. When you intercepted the IRP_MJ_DEVICE_CONTROL handler, you interrupted that communication, thus causing the house of cards to collapse. The change you need to make is in your MyDeviceControl handler. If you receive a request that you don't recognize, you don't complete it. Instead, you do return PcDispatchIrp( DeviceObject,irp );. That lets port class do its normal processing, as if you hadn't been there.

    The same thing applies to all requests that you intercept. If you don't want it, call PcDispatchIrp. Don't just blindly complete it.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • hpgruberhpgruber Member Posts: 8

    Thanks for your response.

     

    I'm now only processing requests that have the control code I am interested in, and relay the others to Port class:

     

    #define MY_DEVICE 0x8000
    #define IOCTL_MY_CONTROLCODE CTL_CODE(MY_DEVICE, 0x8009, METHOD_BUFFERED, FILE_ANY_ACCESS)
    
    NTSTATUS MyDeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP irp)
    {
        auto stack = IoGetCurrentIrpStackLocation(irp);
    
        switch (stack->Parameters.DeviceIoControl.IoControlCode)
        {
        case IOCTL_MY_CONTROLCODE: {
            DbgPrint("I was here!");
            auto status = STATUS_SUCCESS;
            irp->IoStatus.Status = status;
            irp->IoStatus.Information = 0;
            IoCompleteRequest(irp, IO_NO_INCREMENT);
            return status;
        }
        default:
            return PcDispatchIrp(DeviceObject, irp);
        }
    }
    

    The system still crashes when I install the driver. As a sanity check, If i out-comment the

    // DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDeviceControl;
    

    the driver installs fine.

     

    The above code is the only thing I changed in the sample, I did not touch other code.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,448

    OK... so, you have a debugging task to do, right?

    Set a breakpoint in MyDeviceControl. Single-step into the code, line by line.

    If it doesn't MAKE it to MyDeviceControl, get the output from !analyze -v and post it here. Otherwise, your problem will be solved.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,939

    That's the right recipe. Are you using some wonky compiler options that are giving you the wrong linkage? If you add this before the function:

    DRIVER_DISPATCH MyDeviceControl;
    

    Does the compiler complain?

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • hpgruberhpgruber Member Posts: 8

    Just saw your post - you were spot on Tim.

     

    First of all, thanks Peter for pointing me to the MEMORY.DMP file. I completely forgot I could do that. I guess this is what happens when you just learn about Kernel programming.

     

    The first !analyse -v didn't give me much insight except stating that I referenced invalid system memory, so I enabled Driver Verifier and ticked all check boxes. Then something interesting came up:

    nt!KeBugCheckEx
    nt!MiSystemFault+0x1f4791
    nt!MmAccessFault+0x400
    nt!KiPageFault+0x35e
    simpleaudiosample+0x1d360
    VerifierExt!xdv_IRP_MJ_DEVICE_CONTROL_wrapper+0x16c
    nt!IopfCallDriver+0x53
    nt!IovCallDriver+0x266
    nt!IofCallDriver+0x1dbea9
    nt!ViFilterDispatchGeneric+0xbf
    nt!IopfCallDriver+0x53
    nt!IovCallDriver+0x266
    nt!IofCallDriver+0x1dbea9
    ksthunk!CKernelFilterDevice::DispatchIrp+0x236
    ksthunk!CKernelFilterDevice::DispatchIrpBridge+0x13
    nt!IopfCallDriver+0x53
    
    

    Plus I got:

    FAILURE_BUCKET_ID:  AV_VRF_INVALID_BAD_IP_simpleaudiosample!unknown_function
    

     

    Unknown function? IRP_MJ_DEVICE_CONTROL_wrapper? I did some research and found out that Visual Studio has a code analysis tool that does additional checks, checks that are usually not enabled if I just build the solution (never used Visual Studio before). Enabling this gave me one additional warning:

    Warning C28169
    
    The dispatch function 'MyDeviceControl' does not have any _Dispatch_type_ annotations: 
    This can be corrected by adding appropriate _Dispatch_type_ annotations to the function.
    Unnecessary casts can cause this warning.
    Main    C:\dev\simpleaudiosample\Source\Main\adapter.cpp    368
    

     

    Fascinating, I had never heard about those annotations before. In fact, I was reading "Windows Kernel Programming" by Pavel Yosifovich, and these annotations are not mentioned in the book. But good old CTRL-F pointed me to the annotation of the PnpHandler function that came with the sample and was hiding somewhere in the adapter.cpp file:

    _Dispatch_type_(IRP_MJ_PNP)
    DRIVER_DISPATCH PnpHandler;
    

     

    So I did the same for my function. After that the analysis tool gave me some warnings about the signature of my function so I changed

    NTSTATUS MyDeviceControl(_In_ PDEVICE_OBJECT DeviceObject,
        _In_ PIRP irp)
    

    to

    NTSTATUS MyDeviceControl(_In_ DEVICE_OBJECT *DeviceObject,
        _Inout_ IRP *irp)
    

     

    After that, all the warnings vanished and I was just left with the 84 warnings that came with the project :) The driver now installs fine, and I even get my debug prints when my handler gets called!

  • hpgruberhpgruber Member Posts: 8

    So, I progressed quite a bit and I'm now able to get data into the miniport driver reliably. But I have some questions I did not find answers to.
     
    I am streaming audio over a local network via UDP. My client app receives the packets and feeds it into the driver (IOCTL -> Ringbuffer, Ringbuffer -> m_pDmaBuffer). It needs to do so at a relatively constant speed, since I am simulating hardware here. I say relatively because the Ringbuffer will help me here. As far as I understand, there is a timer that ends up calling UpdatePosition which calculates how many bytes need to be written to the dma buffer, based on the format and how much time has passed. Then from the WriteBytes function I take exactly this amount of bytes out from my ring buffer and put it into the dma buffer. So far so good.
     
    But again, I am streaming over UDP. Some packets will get lost. Which means that at some point, I will fall behind and my ring buffer will deplete. No amount of buffering can save me here. Now of course I can enumerate the packets and then for each lost packet, fill in zeros, but then I don't have contiguous audio which will probably sound awful. What actually happens if I decide to modify the UpdatePosition function and at certain iterations write a few less than the "supposed" number of bytes? What happens if I write more than the calculated amount of bytes? Can I mess with that? Obviously the main buffering will happen in the Ringbuffer and/or in the client app, I am talking about teeny tiny adjustments.

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,939

    But again, I am streaming over UDP. Some packets will get lost.

    Theoretically, yes. In practice, this almost never happens.

    Now of course I can enumerate the packets and then for each lost packet, fill in zeros, but then I don't have contiguous audio which will probably sound awful.

    Yes, but since this would only occur in extraordinary circumstances, in my mind the risk is low.

    What actually happens if I decide to modify the UpdatePosition function...

    So, do what an NTP server does, and tweak the byte count a little at a time until you catch up? In practice, this probably works fine. The HLK tests do some very precise throughput analysis and would probably be irritated by that, but the audio subsystem itself is prepared to handle devices whose clocks are a few Hertz off of normal.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • hpgruberhpgruber Member Posts: 8

    Thanks, I made it work. The driver works flawlessly on Windows 10.

     

    But when I set the Target OS to Windows 7, I get literally hundreds of compiler errors. Now, some of those I can solve, for example by using ExAllocatePoolUninitialized instead of ExAllocatePool2, as well as the corresponding POOL_FLAGS argument issue.

     

    But for most errors I don't stand a chance. Short excerpt:

     

    1>C:\dev\simpleaudiosample\Source\Filters\micarraytopo.h(88,21): error C2061: syntax error: identifier 'POOL_FLAGS'
    1>C:\dev\simpleaudiosample\Source\Filters\micarray1toptable.h(178,9): error C2065: 'KSPROPERTY_AUDIO_MIC_SNR': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Filters\micarray1toptable.h(184,9): error C2065: 'KSPROPERTY_AUDIO_MIC_SENSITIVITY2': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Filters\minipairs.h(32,17): error C2061: syntax error: identifier 'POOL_FLAGS'
    1>C:\dev\simpleaudiosample\Source\Filters\minipairs.h(44,17): error C2061: syntax error: identifier 'POOL_FLAGS'
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(187,35): error C3861: 'ExAllocatePool2': identifier not found
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(244,36): error C3861: 'ExAllocatePool2': identifier not found
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(701,5): error C2065: 'PPORTCLSStreamResourceManager': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(701,35): error C2146: syntax error: missing ';' before identifier 'pPortClsResMgr'
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(701,35): error C2065: 'pPortClsResMgr': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(702,5): error C2065: 'PPORTCLSStreamResourceManager2': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(702,36): error C2146: syntax error: missing ';' before identifier 'pPortClsResMgr2'
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(702,36): error C2065: 'pPortClsResMgr2': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(768,48): error C2065: 'IID_IPortClsStreamResourceManager': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(768,93): error C2065: 'pPortClsResMgr': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(771,13): error C2065: 'PCSTREAMRESOURCE_DESCRIPTOR': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(771,41): error C2146: syntax error: missing ';' before identifier 'res'
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(771,41): error C2065: 'res': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(772,13): error C2065: 'PCSTREAMRESOURCE': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(772,30): error C2146: syntax error: missing ';' before identifier 'hRes'
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(772,30): error C2065: 'hRes': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(776,47): error C2065: 'res': undeclared identifier
    1>C:\dev\simpleaudiosample\Source\Main\adapter.cpp(776,13): error C3861: 'PCSTREAMRESOURCE_DESCRIPTOR_INIT': identifier not found
    

     

    Am I missing something here? My driver is based on the "simpleaudiodriver" from Microsoft's samples. I find it hard to believe that the Microsoft driver guys would publish a simple example that fails to target 20% of Windows machines.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,448

    I find it hard to believe that the Microsoft driver guys would publish a simple example that fails to target 20% of Windows machines

    I mean this with no sarcasm or disrespect: But if you find it hard to believe that a current Windows driver sample doesn't work on Win7, you haven't been around much.

    As, I think Mr. Roberts said here recently, in Redmond when it comes to drivers the only version of Windows that really exists is the one that is ABOUT to be released. Except for a few extraordinary folks in the WDK team, most MSFT folks don't really have time or interest to think about making drivers backward compatible to Win 7. Win 7 is dead to them. Witness the whole controversy of driver cross-signing on Windows 7.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • hpgruberhpgruber Member Posts: 8

    I mean this with no sarcasm or disrespect: But if you find it hard to believe that a current Windows driver sample doesn't work on Win7, you haven't been around much.

     

    You are correct. I haven't been around much, in fact I'm new to Windows programming as well as driver/kernel programming. No offense taken :smile:

     

    So no, I didn't know about Microsoft's stance, thanks for telling me. Their docs made it seem quite simple to target earlier OSes, so I thought I missed some obvious step when it comes to target Win 7. But as this seems not to be the case, I will just have to try and hunt down each and every one of those errors.

     

    Hans-Peter

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,448

    Thanks for not taking offense, because none was intended.

    First let me say that I know less than anybody on here about audio devices. But, just taking one of the system data structures that you're seeing as undefined (PCSTREAMRESOURCE_DESCRIPTOR), and looking in the WDK docs for info about that structure, I see this:

    ...which indicates this is a new structure with Windows 10.

    So... yeah. No Win 7 support.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,939

    The "SimpleAudioSample" is brand new. Sysvad has the advantage of being derived from MSVAD, which has a lineage dating back to the 20th Century. Although it has new features, most of them are fairly well contained. "SimpleAudioSample" was created from scratch in the new world. The author made no attempts to make it backwards compatible.

    The other direction is almost always easy. The MSVAD sample still works, and indeed that's what I use as the basis for the virtual audio drivers I create.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • hpgruberhpgruber Member Posts: 8

    I am now targeting Windows 10 only. Microsoft won, once again.

     

    Anyway, everything is working correctly using 16-bit PCM. However, I want to use 32-bit floats for my capture device.

     

    So I changed the micarraywavtable.h accordingly:

     

    static
    KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArrayPinSupportedDeviceFormats[] =
    {
        {
            {
                sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE),
                0,
                0,
                0,
                STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
                STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT),  // before: KSDATAFORMAT_SUBTYPE_PCM
                STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
            },
            {
                {
                    WAVE_FORMAT_EXTENSIBLE,
                    1,
                    48000,
                    192000, // before: 96000
                    4, // before: 2
                    32, // before : 16
                    sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)
                },
                32, // before: 16
                KSAUDIO_SPEAKER_MONO,
                STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)  // before: KSDATAFORMAT_SUBTYPE_PCM
            }
        }
    };
    

    as well as

    static
    KSDATARANGE_AUDIO MicArrayPinDataRangesRawStream[] =
    {
        {
            {
                sizeof(KSDATARANGE_AUDIO),
                KSDATARANGE_ATTRIBUTES, 
                0,
                0,
                STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
                STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT), // before: KSDATAFORMAT_SUBTYPE_PCM
                STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
            },
            MICARRAY_RAW_CHANNELS,
            MICARRAY_BITS_PER_SAMPLE,
            MICARRAY_BITS_PER_SAMPLE,
            MICARRAY_RAW_SAMPLE_RATE,
            MICARRAY_RAW_SAMPLE_RATE
        },
    }
    

    Of course I also changed the MICARRAY_BITS_PER_SAMPLE value accordingly.

     

    But the device never shows up. What surprised me is that CMiniportWaveRT::IsFormatSupported is never even being called. Some tracing showed that the function MinWaveRT::PropertyHandler_Wavefilter is called 31 times when I use 16-bit integers, but only 19 times when using 32-bit floats. I don't know why and the call hierarchy ends here in my driver, these functions are called by some mystical entity I don't know about.

     

    After searching the wdmaudiodev mailing list I am actually unsure if it is possible to implement a capture device using KSDATAFORMAT_SUBTYPE_IEEE_FLOAT. Some tried and failed. On the other hand, Tim Roberts said at some point he is mainly working with 32-bit floats, though I am not sure if he was referring to capture devices.

     

    So, if this can be done, what am I missing?

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Developing Minifilters 24 May 2021 Live, Online
Writing WDF Drivers 14 June 2021 Live, Online
Internals & Software Drivers 27 September 2021 Live, Online
Kernel Debugging TBD 2021 Live, Online