question

Morgan-4747 avatar image
0 Votes"
Morgan-4747 asked ·

[Windows Hello WBDI/EngineAdapter development] A question about Adapter Workflow

Hi All,
I got a problem when I developed the WBDI and EngineAdapter on my fingerprint module.
The problem is why the EngineAdapter doesn't go into below callback functions after I finished Step 1 and Step 2 ?
- "EngineAdapterCreateEnrollment"
- "EngineAdapterSetEnrollmentParameters"

Note:
(1) Due to the document of Microsoft mentioned that upon functions will be called when enrollment workflow goes through.
(https://docs.microsoft.com/zh-tw/windows/win32/secbiomet/adapter-workflow)

(2) The log "EngineAdapter log" that is some funcions be called in EngineAdapter when I finished Step 1 and Step 2.

(3) The log "WBDI log" that is the WBDI receives some IOCTL from windows biometric framework when I finished Step 1 and Step 2.

Thanks


Step 1
78675-step-1.png


Step 2
78619-step-2.png

Adapter Workflow
78620-adapterworkflow.png



"EngineAdapter log"
DllMain, Wed Mar 17 14:22:37 2021
WbioQueryEngineInterface, Wed Mar 17 14:22:37 2021
EngineAdapterAttach, Wed Mar 17 14:22:37 2021
EngineAdapterSelectCalibrationFormat, Wed Mar 17 14:22:37 2021
EngineAdapterPipelineInit, Wed Mar 17 14:22:37 2021
DllMain, Wed Mar 17 14:22:42 2021
EngineAdapterActivate, Wed Mar 17 14:22:42 2021
EngineAdapterQueryExtendedInfo, Wed Mar 17 14:22:42 2021
EngineAdapterSetAccountPolicy, Wed Mar 17 14:22:42 2021
DllMain, Wed Mar 17 14:22:43 2021
EngineAdapterClearContext, Wed Mar 17 14:22:45 2021
EngineAdapterQueryPreferredFormat, Wed Mar 17 14:22:45 2021
EngineAdapterCreateKey, Wed Mar 17 14:22:46 2021
EngineAdapterCreateKey, Wed Mar 17 14:22:46 2021
EngineAdapterAcceptSampleData, Wed Mar 17 14:22:46 2021
EngineAdapterIdentifyFeatureSet, Wed Mar 17 14:22:46 2021
EngineAdapterClearContext, Wed Mar 17 14:22:46 2021
EngineAdapterClearContext, Wed Mar 17 14:22:46 2021


"WBDI log"
IOCTL_BIOMETRIC_GET_ATTRIBUTES, Wed Mar 17 14:22:37 2021
IOCTL_BIOMETRIC_GET_SENSOR_STATUS, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_GET_ATTRIBUTES, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_CAPTURE_DATA, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_CAPTURE_DATA, Wed Mar 17 14:22:45 2021
IOCTL_BIOMETRIC_GET_SENSOR_STATUS, Wed Mar 17 14:22:46 2021


EngineAdapter code
static WINBIO_ENGINE_INTERFACE g_EngineInterface = {
WINBIO_ENGINE_INTERFACE_VERSION_6,
WINBIO_ADAPTER_TYPE_ENGINE,
sizeof(WINBIO_ENGINE_INTERFACE),
{0xb876fdc8, 0x34e7, 0x471a, {0x82, 0xc8, 0x9c, 0xba, 0x6a, 0x35, 0x38, 0xec}},

 EngineAdapterAttach,
 EngineAdapterDetach,
 EngineAdapterClearContext,
 EngineAdapterQueryPreferredFormat,
 EngineAdapterQueryIndexVectorSize,
 EngineAdapterQueryHashAlgorithms,
 EngineAdapterSetHashAlgorithm,
 EngineAdapterAcceptSampleHint,
 EngineAdapterAcceptSampleData,
 EngineAdapterExportEngineData,
 EngineAdapterVerifyFeatureSet,
 EngineAdapterIdentifyFeatureSet,
 EngineAdapterCreateEnrollment,
 EngineAdapterUpdateEnrollment,
 EngineAdapterGetEnrollmentStatus,
 EngineAdapterGetEnrollmentHash,
 EngineAdapterCheckForDuplicate,
 EngineAdapterCommitEnrollment,
 EngineAdapterDiscardEnrollment,
 EngineAdapterControlUnit,
 EngineAdapterControlUnitPrivileged,

  EngineAdapterNotifyPowerChange,
  EngineAdapter_RESERVED,


 EngineAdapterPipelineInit,
 EngineAdapterPipelineCleanup,
 EngineAdapterActivate,
 EngineAdapterDeactivate,
 EngineAdapterQueryExtendedInfo,
 EngineAdapterIdentifyAll,
 EngineAdapterSetEnrollmentSelector,
 EngineAdapterSetEnrollmentParameters,
 EngineAdapterQueryExtendedEnrollmentStatus,
 EngineAdapterRefreshCache,
 EngineAdapterSelectCalibrationFormat,
 EngineAdapterQueryCalibrationData,
 EngineAdapterSetAccountPolicy,


 EngineAdapterCreateKey,
 EngineAdapterIdentifyFeatureSetSecure,



 EngineAdapterAcceptPrivateSensorTypeInfo,

 EngineAdapterCreateEnrollmentAuthenticated,
 EngineAdapterIdentifyFeatureSetAuthenticated

};






WBDI code
void
CBiometricDevice::OnGetAttributes(
Inout IWDFIoRequest *FxRequest
)

{
CRequestHelper MyRequest(FxRequest); // RAII helper class
ULONG controlCode = 0;
PUCHAR inputBuffer= NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_SENSOR_ATTRIBUTES sensorAttributes = NULL;
SIZE_T outputBufferSize;

 //
 // Get the request parameters
 //
 GetIoRequestParams(FxRequest,
                   &controlCode,
                   &inputBuffer,
                   &inputBufferSize,
                   (PUCHAR *)&sensorAttributes,
                   &outputBufferSize);

 //
 // Make sure we have an output buffer big enough
 //
 if (sensorAttributes == NULL || outputBufferSize < sizeof(DWORD)) 
 {
     // We cannot return size information.
     TraceEvents(TRACE_LEVEL_ERROR, 
                BIOMETRIC_TRACE_DEVICE, 
                "%!FUNC!Output buffer NULL or too small to return size information.");
     MyRequest.SetCompletionHr(E_INVALIDARG);
     return;
 }

 // We only have one supported format, so sizeof (WINBIO_SENSOR_ATTRIBUTES) is sufficient.
 if (outputBufferSize < sizeof(WINBIO_SENSOR_ATTRIBUTES)) 
 {
     // Buffer too small.
     TraceEvents(TRACE_LEVEL_ERROR, 
                BIOMETRIC_TRACE_DEVICE, 
                "%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_SENSOR_ATTRIBUTES));
     sensorAttributes->PayloadSize = (DWORD) sizeof(WINBIO_SENSOR_ATTRIBUTES);
     MyRequest.SetInformation(sizeof(DWORD));
     MyRequest.SetCompletionHr(S_OK);
     return;
 }

 //
 // Fill in the attribute payload structure
 //
 RtlZeroMemory(sensorAttributes, outputBufferSize);
 sensorAttributes->PayloadSize = (DWORD) sizeof(WINBIO_SENSOR_ATTRIBUTES);
 sensorAttributes->WinBioHresult = S_OK;
 sensorAttributes->WinBioVersion.MajorVersion = WINBIO_WBDI_MAJOR_VERSION;
 sensorAttributes->WinBioVersion.MinorVersion = WINBIO_WBDI_MINOR_VERSION;
 sensorAttributes->SensorType = WINBIO_TYPE_FINGERPRINT;
 sensorAttributes->SensorSubType = WINBIO_FP_SENSOR_SUBTYPE_TOUCH;// WINBIO_FP_SENSOR_SUBTYPE_SWIPE;//WINBIO_FP_SENSOR_SUBTYPE_TOUCH;
 sensorAttributes->Capabilities = WINBIO_CAPABILITY_SENSOR | WINBIO_CAPABILITY_MATCHING | WINBIO_CAPABILITY_DATABASE;// WINBIO_CAPABILITY_SENSOR | WINBIO_CAPABILITY_MATCHING | WINBIO_CAPABILITY_DATABASE | WINBIO_CAPABILITY_PROCESSING;// WINBIO_CAPABILITY_SENSOR;
 sensorAttributes->SupportedFormatEntries = 1;
 sensorAttributes->SupportedFormat[0].Owner = WINBIO_ANSI_381_FORMAT_OWNER;
 sensorAttributes->SupportedFormat[0].Type= WINBIO_ANSI_381_FORMAT_TYPE;
 RtlCopyMemory(sensorAttributes->ManufacturerName, SAMPLE_MANUFACTURER_NAME, (wcslen(SAMPLE_MANUFACTURER_NAME)+1)*sizeof(WCHAR));
 RtlCopyMemory(sensorAttributes->ModelName, SAMPLE_MODEL_NAME, (wcslen(SAMPLE_MODEL_NAME)+1)*sizeof(WCHAR));
 RtlCopyMemory(sensorAttributes->SerialNumber, SAMPLE_SERIAL_NUMBER, (wcslen(SAMPLE_SERIAL_NUMBER)+1)*sizeof(WCHAR));
 sensorAttributes->FirmwareVersion.MajorVersion = 1;
 sensorAttributes->FirmwareVersion.MinorVersion = 0;

 MyRequest.SetInformation(sensorAttributes->PayloadSize);
 MyRequest.SetCompletionHr(S_OK);

}

void
CBiometricDevice::OnGetSensorStatus(
Inout IWDFIoRequest *FxRequest
)
{
CRequestHelper MyRequest(FxRequest); // RAII helper class
ULONG controlCode = 0;
PUCHAR inputBuffer= NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_DIAGNOSTICS diagnostics = NULL;
SIZE_T outputBufferSize;

 //
 // Get the request parameters
 //
 GetIoRequestParams(FxRequest,
                   &controlCode,
                   &inputBuffer,
                   &inputBufferSize,
                   (PUCHAR *)&diagnostics,
                   &outputBufferSize);

 //
 // Make sure we have an output buffer big enough
 //
 if (diagnostics == NULL || outputBufferSize < sizeof(DWORD)) 
 {
     // We cannot return size information.
     TraceEvents(TRACE_LEVEL_ERROR, 
                BIOMETRIC_TRACE_DEVICE, 
                "%!FUNC!Output buffer NULL or too small to return size information.");
     MyRequest.SetCompletionHr(E_INVALIDARG);
     return;
 }

 if (outputBufferSize < sizeof(WINBIO_DIAGNOSTICS))
 {
     // Buffer too small.
     TraceEvents(TRACE_LEVEL_ERROR,
         BIOMETRIC_TRACE_DEVICE,
         "%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_DIAGNOSTICS));
     diagnostics->PayloadSize = (DWORD)sizeof(WINBIO_DIAGNOSTICS);
     MyRequest.SetInformation(sizeof(DWORD));
     MyRequest.SetCompletionHr(S_OK);

     return;
 }

 //
 // Fill in the OUT payload structure
 //
 RtlZeroMemory(diagnostics, outputBufferSize);
 diagnostics->PayloadSize = (DWORD) sizeof(WINBIO_DIAGNOSTICS);
 diagnostics->WinBioHresult = S_OK;
 //diagnostics->SensorStatus = WINBIO_SENSOR_READY;

 if (SensorStatusFlag)
 {
     SensorStatusFlag = 0;
     diagnostics->SensorStatus = WINBIO_SENSOR_ACCEPT;
 }
 else
 {
     diagnostics->SensorStatus = WINBIO_SENSOR_READY;
 }

 MyRequest.SetInformation(diagnostics->PayloadSize);
 MyRequest.SetCompletionHr(S_OK);

}

void
CBiometricDevice::OnCaptureData(
Inout IWDFIoRequest *FxRequest
)
{
ULONG controlCode = 0;
PWINBIO_CAPTURE_PARAMETERS captureParams = NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_CAPTURE_DATA captureData = NULL;
SIZE_T outputBufferSize = 0;

 //
 // We can only have one outstanding data capture request at a time.
 // Check to see if we have a request pending.
 //
 bool requestPending = false;

 EnterCriticalSection(&m_RequestLock);

 if (m_PendingRequest == NULL) 
 {
     //
     // See if we have an active sleep thread.
     // If so, tell it to exit.
     // Wait for it to exit.
     //
     if (m_SleepThread != INVALID_HANDLE_VALUE)
     {
         LeaveCriticalSection(&m_RequestLock);

         // TODO: Add code to signal thread to exit.

         // NOTE: Sleeping for INFINITE time is dangerous. A real driver
         // should be able to handle the case where the thread does
         // not exit.
         WaitForSingleObject(m_SleepThread, INFINITE);
         CloseHandle(m_SleepThread);
         m_SleepThread = INVALID_HANDLE_VALUE;

         EnterCriticalSection(&m_RequestLock);
     }

     // 
     // We might have had to leave the CS to wait for the sleep thread.
     // Double check that the pending request is still NULL.
     //
     if (m_PendingRequest == NULL)
     {
         // Save the request.
         m_PendingRequest = FxRequest;

         // Mark the request as cancellable.
         m_PendingRequest->MarkCancelable(this);
     }
     else
     {
         requestPending = true;
     }
 } 
 else 
 {
     requestPending = true;
 }

 LeaveCriticalSection(&m_RequestLock);

 if (requestPending)
 {
     // Complete the request to tell the app that there is already
     // a pending data collection request.
     FxRequest->Complete(WINBIO_E_DATA_COLLECTION_IN_PROGRESS);
     return;
 }

 //
 // Get the request parameters
 //
 GetIoRequestParams(FxRequest,
                    &controlCode,
                    (PUCHAR *)&captureParams,
                    &inputBufferSize,
                    (PUCHAR *)&captureData,
                    &outputBufferSize);

 //
 // Check input parameters.
 //
  if (inputBufferSize < sizeof (WINBIO_CAPTURE_PARAMETERS)) 
  {
     // Invalid arguments
     TraceEvents(TRACE_LEVEL_ERROR, 
                BIOMETRIC_TRACE_DEVICE, 
                "%!FUNC!Invalid argument(s).");
     CompletePendingRequest(E_INVALIDARG, 0);
     return;
 }

 //
 // Make sure we have an output buffer big enough
 //
 if (outputBufferSize < sizeof(DWORD)) 
 {
     // We cannot return size information.
     TraceEvents(TRACE_LEVEL_ERROR, 
                BIOMETRIC_TRACE_DEVICE, 
                "%!FUNC!Output buffer NULL or too small to return size information.");
     CompletePendingRequest(E_INVALIDARG, 0);
     return;
 }

 //
 // Check output buffer size.
 //
 if (outputBufferSize < sizeof (WINBIO_CAPTURE_DATA)) 
 {
     // Buffer too small.
     TraceEvents(TRACE_LEVEL_ERROR, 
                BIOMETRIC_TRACE_DEVICE, 
                "%!FUNC!Buffer too small - must be at least 0x%x.", sizeof (WINBIO_CAPTURE_DATA));
     //
     // NOTE:  The output buffer size necessary for this sample is sizeof(WINBIO_CAPTURE_DATA).
     // Real devices will need additional space to handle a typical capture.
     // The value that should be returned here is sizeof(WINBIO_CAPTURE_DATA) + CaptureBufferSize.
     //

     captureData->PayloadSize = (DWORD)sizeof(WINBIO_CAPTURE_DATA)+12;
     CompletePendingRequest(S_OK, sizeof(DWORD));
     return;
 }

 //
 // NOTE:  This call always fails in this sample since it is not
 // written for a real device.
 //

 //
 // Set default values in output buffer.
 //
 SensorStatusFlag = 1;
 RtlZeroMemory(captureData, outputBufferSize);
 captureData->PayloadSize = (DWORD)outputBufferSize;

 captureData->WinBioHresult = S_OK;
 captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
 captureData->RejectDetail = 0;
 captureData->CaptureData.Size = (DWORD)12;
    
 UCHAR szBuffer[] = { 0x01, 0x02, 0x03, 0x04 ,0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04 };
 RtlCopyMemory(captureData->CaptureData.Data, szBuffer,12);

 //
 // Check purpose, format and type.
 //
 if (captureParams->Purpose == WINBIO_NO_PURPOSE_AVAILABLE)
 {
     captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_PURPOSE;
 }
 else if ((captureParams->Format.Type != WINBIO_ANSI_381_FORMAT_TYPE) ||
          (captureParams->Format.Owner != WINBIO_ANSI_381_FORMAT_OWNER))
 {
     captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_FORMAT;
 }
 else if (captureParams->Flags != WINBIO_DATA_FLAG_RAW)
 {
     captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_TYPE;
 }

 //
 // Create thread to sleep 1 seconds before completing the request.
 //
 m_SleepParams.SleepValue = 1;
 m_SleepParams.Hr = S_OK;
 m_SleepParams.Information = captureData->PayloadSize;
 m_SleepThread = CreateThread(NULL,                   // default security attributes
                              0,                      // use default stack size  
                              CaptureSleepThread,     // thread function name
                              this,                   // argument to thread function 
                              0,                      // use default creation flags 
                              NULL);                  // returns the thread identifier 

}

DWORD WINAPI
CaptureSleepThread(
LPVOID lpParam
)
{
CBiometricDevice device = (CBiometricDevice ) lpParam;
PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();

 //
 // Make sure it is less than or equal to 1 minute.
 //
 if (sleepParams->SleepValue > 60)
 {
     sleepParams->SleepValue = 60;
 }

 Sleep(sleepParams->SleepValue * 1000);

 device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);

 return 0;

}


winapi-generalwindows-hardware-wdk-generalwindows-hardware-wdk
step-1.png (38.6 KiB)
step-2.png (40.0 KiB)
adapterworkflow.png (97.2 KiB)
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

0 Answers