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

Home NTDEV

Before Posting...

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

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/


Virtual Smart Card Reader error in monitoring card status.

minhptaminhpta Member Posts: 23

Dear experts,

I am developing a virtual smart card reader but I am sticking on make it visible to Smart Card Resource Manager.
My source code is based on this guide **https://docs.microsoft.com/en-us/windows-hardware/drivers/smartcard/ ** and Windows-driver-samples\smartcrd.
However, I just make it very simple as I can understand.

SmartcardDeviceControl return STATUS_DEVICE_BUSY(0x80000011) for IoControlCode IOCTL_SMARTCARD_IS_ABSENT(0x31002C or 3211308 in decimal) in callback routine EvtIoDeviceControl.

I guess this is a problem prevent my reader visible to Smart Card Resource Manager but I don't know how to fix it.
I am new in this field. Any guidance would be really helpful.

Please check my source code in the first comment.

And this is how I install the driver.

devcon install VPJSCReader.inf root\VPJSCReader
Device node created. Install is complete when drivers are installed...
Updating drivers for root\VPJSCReader from c:\Users\minh\Downloads\VPJSCReader\VPJSCReader.inf.
Drivers installed successfully.

Here is debugger's log.

VPJSCReader!DriverEntry: Enter - KMDF Version Built Oct 14 2019 08:50:44
VPJSCReader!DriverEntry: Exit 0
VPJSCReader!VPJSCReaderEvtDeviceAdd: Enter
VPJSCReader!VPJSCReaderEvtDeviceAdd: Exit
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 36 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 36 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 4 IoControlCode 3211272
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211304
VPJSCReader!CBCardTracking: Entry
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 4 InputBufferLength 0 IoControlCode 3211320
VPJSCReaderLibComplete called: status 0, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211308
VPJSCReaderLibComplete called: status 80000011, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211308
VPJSCReaderLibComplete called: status 80000011, Context 00001B784F5051D8
VPJSCReaderEvtIoDeviceControl Queue 0x00001B78487674E8, Request 0x00001B784F5051D8 OutputBufferLength 0 InputBufferLength 0 IoControlCode 3211308
VPJSCReaderLibComplete called: status 80000011, Context 00001B784F5051D8

In the last three IO request, it return status 80000011.

Here is my device manager.

And here is my event viewer.

Comments

  • minhptaminhpta Member Posts: 23

    My driver source code.

    NTSTATUS
    DriverEntry(
        _In_ PDRIVER_OBJECT  DriverObject,
        _In_ PUNICODE_STRING RegistryPath
        )
    {
        WDF_DRIVER_CONFIG config;
        NTSTATUS status;
            SmartcardSetDebugLevel(DEBUG_ALL);
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!DriverEntry: Enter - KMDF Version Built %s %s\n",
                __DATE__, __TIME__)
        );
            WDF_DRIVER_CONFIG_INIT(&config,
                               VPJSCReaderEvtDeviceAdd
                               );
            status = WdfDriverCreate(DriverObject,
                                 RegistryPath,
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &config,
                                 WDF_NO_HANDLE
                                 );
            if (!NT_SUCCESS(status)) {
            KdPrint(("WdfDriverCreate failed with status 0x%x\n", status));
            return status;
        }
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!DriverEntry: Exit %x\n",
                status)
        );
            return status;
    }
    NTSTATUS
    VPJSCReaderEvtDeviceAdd(
        _In_    WDFDRIVER       Driver,
        _Inout_ PWDFDEVICE_INIT DeviceInit
        )
    {
        NTSTATUS status;
            UNREFERENCED_PARAMETER(Driver);
            PAGED_CODE();
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!VPJSCReaderEvtDeviceAdd: Enter\n")
        );
            status = VPJSCReaderCreateDevice(DeviceInit);
            SmartcardDebug(
            DEBUG_TRACE,
            ("VPJSCReader!VPJSCReaderEvtDeviceAdd: Exit\n")
        );
            return status;
    }
    NTSTATUS
    VPJSCReaderCreateDevice(
        _Inout_ PWDFDEVICE_INIT DeviceInit
        )
    {
        WDF_OBJECT_ATTRIBUTES deviceAttributes;
        PDEVICE_CONTEXT deviceContext = NULL;
        WDFDEVICE device;
        NTSTATUS status;
            PAGED_CODE();
            WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SMARTCARD);
        WdfDeviceInitSetExclusive(DeviceInit, TRUE);
        WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
            WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
        deviceAttributes.EvtCleanupCallback = VPJSCReaderEvtDriverContextCleanup;
            status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
            if (NT_SUCCESS(status)) {       
            deviceContext = DeviceGetContext(device);
                deviceContext->Device = device;
            InterlockedIncrement((PLONG)& deviceContext->DeviceInstanceNo);
                status = WdfDeviceCreateDeviceInterface(
                device,
                &SmartCardReaderGuid,
                NULL // ReferenceString
                );
                if (NT_SUCCESS(status)) {
                status = VPJSCReaderQueueInitialize(device);
            }
        }
            if (!NT_SUCCESS(status)) {
            return status;
        }
                return VPJSCReaderRegisterWithSmcLib(deviceContext);
    }
    NTSTATUS
    VPJSCReaderRegisterWithSmcLib(
        PDEVICE_CONTEXT DeviceContext
    ) {
        PREADER_EXTENSION ReaderExtension;
        PSMARTCARD_EXTENSION SmartcardExtension;
        NTSTATUS status;
            PAGED_CODE();
            //   set up the device extension.
        SmartcardExtension = &DeviceContext->SmartcardExtension;
            //   allocate the reader extension
        ReaderExtension = ExAllocatePoolWithTag(
            NonPagedPoolNx,
            sizeof(READER_EXTENSION),
            SMARTCARD_POOL_TAG
        );
            if (ReaderExtension == NULL) {
            SmartcardLogError(
                WdfDriverWdmGetDriverObject(WdfGetDriver()),
                STATUS_INSUFFICIENT_RESOURCES,
                NULL,
                0
            );
            status = STATUS_INSUFFICIENT_RESOURCES;
            return status;
        }
            RtlZeroMemory(ReaderExtension, sizeof(READER_EXTENSION));
        SmartcardExtension->ReaderExtension = ReaderExtension;
            //   setup smartcard extension - callback's
        SmartcardExtension->ReaderFunction[RDF_CARD_POWER] = CBCardPower;
        SmartcardExtension->ReaderFunction[RDF_TRANSMIT] = CBTransmit;
        SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING] = CBCardTracking;
        SmartcardExtension->ReaderFunction[RDF_SET_PROTOCOL] = CBSetProtocol;
            RtlCopyMemory( SmartcardExtension->VendorAttr.VendorName.Buffer, VPJSC_VENDOR_NAME, sizeof(VPJSC_VENDOR_NAME));
        SmartcardExtension->VendorAttr.VendorName.Length = sizeof(VPJSC_VENDOR_NAME);
            RtlCopyMemory(SmartcardExtension->VendorAttr.IfdType.Buffer, VPJSC_IFD_TYPE, sizeof(VPJSC_IFD_TYPE));
        SmartcardExtension->VendorAttr.IfdType.Length = sizeof(VPJSC_IFD_TYPE);
            SmartcardExtension->VendorAttr.UnitNo = DeviceContext->DeviceInstanceNo;
            SmartcardExtension->VendorAttr.IfdVersion.BuildNumber = 0;
            //   store firmware revision in ifd version
        SmartcardExtension->VendorAttr.IfdVersion.VersionMajor = 0;
        SmartcardExtension->VendorAttr.IfdVersion.VersionMinor = 0;
        SmartcardExtension->VendorAttr.IfdSerialNo.Length = 0;
            //   setup smartcard extension - reader capabilities
        SmartcardExtension->ReaderCapabilities.SupportedProtocols = SCARD_PROTOCOL_T0;
            SmartcardExtension->ReaderCapabilities.ReaderType = SCARD_READER_TYPE_VENDOR;
            SmartcardExtension->ReaderCapabilities.MechProperties = 0;
        SmartcardExtension->ReaderCapabilities.Channel = 0;
            SmartcardExtension->ReaderCapabilities.CLKFrequency.Default = 4000;
        SmartcardExtension->ReaderCapabilities.CLKFrequency.Max = 4000;
            // reader could support higher data rates
        SmartcardExtension->ReaderCapabilities.DataRatesSupported.List = 0;
        SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries = 0;
        SmartcardExtension->ReaderCapabilities.DataRate.Default = 9600;
        SmartcardExtension->ReaderCapabilities.DataRate.Max = 9600;
            SmartcardExtension->Version = SMCLIB_VERSION;
        SmartcardExtension->SmartcardRequest.BufferSize = MIN_BUFFER_SIZE;
        SmartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE;
            SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT;
            status = SmartcardInitialize(SmartcardExtension);
        if (status != STATUS_SUCCESS) {
                SmartcardLogError(
                WdfDriverWdmGetDriverObject(WdfGetDriver()),
                STATUS_INSUFFICIENT_RESOURCES,
                NULL,
                0
            );
            return status;
        }
            SmartcardExtension->OsData->DeviceObject = WdfDeviceWdmGetDeviceObject(DeviceContext->Device);
            return status;
    }
    
    NTSTATUS
    CBCardPower(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE,("VPJSCReader!CBCardPower: Entry\n"));   
        return STATUS_NO_MEDIA;
    }
    NTSTATUS
    CBSetProtocol(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBSetProtocol: Entry\n"));
        return STATUS_NO_MEDIA;
    }
    NTSTATUS
    CBTransmit(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBTransmit: Entry\n"));
        return STATUS_NO_MEDIA;
    }
    NTSTATUS
    CBCardTracking(
        PSMARTCARD_EXTENSION SmartcardExtension
    ) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBCardTracking: Entry\n"));
        return STATUS_SUCCESS;
    }
    
    
    _Function_class_(IO_COMPLETION_ROUTINE)
    NTSTATUS
    VPJSCReaderLibComplete(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp,
        IN PVOID Context
    )
    {
        UNREFERENCED_PARAMETER(DeviceObject);
    
        KdPrint(("VPJSCReaderLibComplete called: status %x, Context %p\n", Irp->IoStatus.Status, Context));
    
        WdfRequestComplete((WDFREQUEST)Context, Irp->IoStatus.Status);
    
        return STATUS_MORE_PROCESSING_REQUIRED;
    }
    
    VOID
    VPJSCReaderEvtIoDeviceControl(
        _In_ WDFQUEUE Queue,
        _In_ WDFREQUEST Request,
        _In_ size_t OutputBufferLength,
        _In_ size_t InputBufferLength,
        _In_ ULONG IoControlCode
        )
    {    
        PDEVICE_CONTEXT deviceExtension;
        PIRP irp;
    
        KdPrint(("VPJSCReaderEvtIoDeviceControl Queue 0x%p, Request 0x%p OutputBufferLength %d InputBufferLength %d IoControlCode %d\n",
            Queue, Request, (int) OutputBufferLength, (int) InputBufferLength, IoControlCode));
    
        deviceExtension = DeviceGetContext(WdfIoQueueGetDevice(Queue));
    
        irp = WdfRequestWdmGetIrp(Request);
    
        SET_REQUEST_IN_IRP(irp, Request);
    
        IoCopyCurrentIrpStackLocationToNext(irp);
    
        IoSetCompletionRoutine(irp,
            VPJSCReaderLibComplete,
            Request,
            TRUE,
            TRUE,
            TRUE
        );
    
        IoSetNextIrpStackLocation(irp);
    
        (VOID)SmartcardDeviceControl(&(deviceExtension->SmartcardExtension), irp);
    
        return;
    }
    
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 14,852

    There are a few problems here. Have you done any reading at all about the functions you are calling?

    I don't understand why you would use IoSetCompletionRoutine instead of WdfRequestSetCompletionRoutine. When an I/O completion routine is finished with an IRP, as yours is, you need to return the status from the IRP, and not STATUS_MORE_PROCESSING_REQUIRED. WdfRequestSetCompletionRoutine would have handled that for you.

    However, at a higher level, the whole completion routine processing is totally wrong. SmartcardDeviceControl is NOT passing the request into another driver. Go look at the documentation page. You do not need to worry about a stack location and a completion routine. That whole routine can be condensed to
    deviceExtension = DeviceGetContext...
    irp = WdfRequestWdmGetIrp(Request);
    return SmartcardDeviceControl(&(deviceExtension->SmartcardExtension), irp);

    Are you getting calls to your callbacks? SmartcardDeviceControl handles requests that don't need hardware, but anything that does need hardware has to call into your callbacks.

    Why did you SetExclusive on the device object? Did you read that somewhere?

    Your InterlockedIncrement of DeviceInstanceNo is silly. That routine will be called exactly once for each device, and each one will get a fresh device context. Thus, that number will never be anything other than 1.

    Tim Roberts, [email protected]
    Software Wizard Emeritus

  • minhptaminhpta Member Posts: 23

    Hi @Tim_Roberts,
    Thank you very much for your comments.

    My source code is based on some resources such as https://docs.microsoft.com/en-us/windows-hardware/drivers/smartcard and Windows-driver-samples\smartcrd. I know this is not the whole picture and I've just focused on some functions related to smart card, not all of them.
    For me, there is a huge amount of knowledge in this filed and I've not understand it clearly.

    Are you getting calls to your callbacks? SmartcardDeviceControl handles requests that don't need hardware, but anything that does need hardware has to call into your callbacks.

    Yes, as you see in debugger's log, routine CBCardTracking is called. If I change

    SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT;
    

    to

    SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_POWERED;
    

    routine CBCardPower is also getting called.

    Now, I remove redundant code as your comments and the driver run same as it before changed.

    The problem may be in this callback routine CBCardTracking:

    NTSTATUS
        CBCardTracking(PSMARTCARD_EXTENSION SmartcardExtension) {
        UNREFERENCED_PARAMETER(SmartcardExtension);
        SmartcardDebug(DEBUG_TRACE, ("VPJSCReader!CBCardTracking: Entry\n"));
        return STATUS_SUCCESS;
    }
    

    Because, I do nothing here, just return success.
    I think that I need to do something with SmartcardExtension->OsData->NotificationIrp but I don't know how.

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 14,852

    OK, so you've established that the framework is fundamentally correct. Now you have to write the guts of your driver. That's where the real work is.

    Tim Roberts, [email protected]
    Software Wizard Emeritus

  • minhptaminhpta Member Posts: 23

    Is there any one here who implement success a virtual smart card reader?
    Please help me, I am still sticking with the problem of my reader is not listed by SCardListReaders.

    My Event IO device control routine VPJSCReaderEvtIoDeviceControl is kept the same in above source code.
    And here what I receive after SmartcardDeviceControl is called(I am using WinDbg to get these information).

    For IoControlCode = IOCTL_SMARTCARD_IS_PRESENT, it returns Irp->IoStatus.Status = STATUS_SUCCESS => same as my expectation.
    For IoControlCode = IOCTL_SMARTCARD_GET_STATE,  it returns Irp->IoStatus.Status = STATUS_SUCCESS, state via Output Buffer = SCARD_ABSENT => same as my expectation.
    For IoControlCode = IOCTL_SMARTCARD_IS_ABSENT, it returns Irp->IoStatus.Status= STATUS_DEVICE_BUSY, not same as my expectation. It should be STATUS_SUCCESS.
    

    I think this problem here is a cause that prevent my reader listed by SCardListReaders. Any idea is welcome!

  • minhptaminhpta Member Posts: 23

    I just come back here to update that the problem has been resolved.
    Sorry for the late update.

  • Eric_WittmayerEric_Wittmayer Member Posts: 50

    Hi @minhpta ,
    Would you mind sharing what the solution was in case someone has the same issue in the future.
    Thanks,
    Eric

  • minhptaminhpta Member Posts: 23

    @Eric_Wittmayer

    Yes, absolutely. I will share It in a completed topic but not now because eventhought my solution works but not as my expectation.
    I have another problem here , It makes my solution is not completed.

  • playxzplayxz Member Posts: 1

    Hi @minhpta
    How did you solve this problem?

    Thanks

  • Alex077Alex077 Member Posts: 2

    Certainly, designing a solid framework is crucial, but the true essence of any driver lies in the implementation details.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. Sign in or register to get started.

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!
Kernel Debugging 9-13 Sept 2024 Live, Online
Developing Minifilters 15-19 July 2024 Live, Online
Internals & Software Drivers 11-15 Mar 2024 Live, Online
Writing WDF Drivers 20-24 May 2024 Live, Online