On-SoC PWM モジュール用 PWM ドライバー

SoC の一部であり、SoC アドレス空間にメモリ マップされたパルス幅変調 (PWM) コントローラーにアクセスできるようにするには、カーネル モード ドライバーを書き込む必要があります。 ドライバーは、UWP アプリが Windows.Devices.Pwm 名前空間で定義されている PWM WinRT API を介してシステム公開 PWM デバイスにアクセスできるように、PWM コントローラーのデバイス クラス インターフェイスを登録する必要があります。

I2C、SPI、または UART コントローラー経由のアドオン PWM モジュールがある場合は、Windows.Devices.Pwm および Windows.Devices.Pwm.Provider 名前空間で定義されている API を使用して、UWP アプリからモジュールにアクセスできます

PWM デバイスは、1 つのコントローラーと 1 つ以上のピンに抽象化されます。 コントローラまたはピンの制御は、PWMで定義されたIOCTLを介して行われます。 たとえば、LCD ディスプレイ ドライバーは、このような要求を PWM ドライバーに送信して、LCD バックライト レベルを制御します。

構成可能な周期、極性、およびデューティ・サイクルを備えたマルチコントローラおよびマルチチャンネル PWM 信号生成により、以下のタスクが可能になります。

  • サーボ モーターを駆動します。
  • LED や DC ブラシ付きモーターなどの負荷を駆動します。
  • アナログ信号を生成します。
  • 正確なクロックを生成します。

このトピックでは、

  • システムで公開されている PWM デバイスへの UWP アクセスを有効にする方法。

  • Win32 アプリケーションまたはサード パーティのカーネル モード ドライバーによって送信された PWM IOCTL 要求を処理する方法。

対象ユーザー

  • OEM と IHV は、SoC 上の PWM コントローラーを備えたシステムを開発しています。

最終日

  • 2017 年 8 月

Windows バージョンの を する

  • Windows 10 のバージョン

重要な API

PWM について

PWMは、波形の平均値の変動をもたらす変調パルス幅を有する矩形パルス波を生成するための基本的な技術を記述する。

PWM波形は、波形周期(T)とデューティサイクルの2つのパラメータで分類できます。 波形周波数(f)は、波形周期f=1/Tの逆数です。 デューティ サイクルは、時間の一定の間隔または "期間" に対する "オン" または "アクティブ" 時間の割合を表します。低デューティ・サイクルは、ほとんどの場合電源がオフになっているため、低出力電力の平均に相当します。 デューティサイクルはパーセントで表され、100% は完全にオン、0% は完全にオフ、50% は時間の 50% で「アクティブ」です。

ドライバー。

システムで公開されている PWM コントローラーとピンへのアクセス

PWM ドライバーを登録する必要があります
PWM デバイスを公開しアクセスするためのデバイス インターフェース GUID として、GUID_DEVINTERFACE_PWM_CONTROLLER が指定されています。

// {60824B4C-EED1-4C9C-B49C-1B961461A819} 

DEFINE_GUID(GUID_DEVINTERFACE_PWM_CONTROLLER, 0x60824b4c, 0xeed1, 0x4c9c, 0xb4, 0x9c, 0x1b, 0x96, 0x14, 0x61, 0xa8, 0x19); 

#define GUID_DEVINTERFACE_PWM_CONTROLLER_WSZ L"{60824B4C-EED1-4C9C-B49C-1B961461A819}" 

デバイス インターフェイス GUID を登録するには、ドライバーは、EVT_WDF_DRIVER_DEVICE_ADDコールバック関数のドライバーの実装で WdfDeviceCreateDeviceInterface を呼び出す必要があります。 登録後、システムはコントローラーとピンにシンボリック リンクを割り当てます。

アプリケーションまたは別のドライバーは、PWM で定義された IOCTL を介してコントローラーまたはピンを制御できます。 IOCTL を送信する前に、送信側アプリケーションまたはドライバーは、シンボリック リンクを指定して、コントローラーとピンへのファイル ハンドルを開く必要があります。 これには、ドライバーがファイルの作成イベントと閉じるイベントを登録する必要があります。 (リンク) を参照してください

コントローラーのシンボリック パスの例を次に示します。

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}  

同様に、ピンの形式は次のとおりです。パスの形式は次のとおりです。

<DeviceInterfaceSymbolicLinkName>\<PinNumber>

ここで <PinNumber> は、開くピンの 0 から始まるインデックスです。

ピンのシンボリック パスの例を次に示します。

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}\0 ; Opens pin 0 

\??\ACPI#FSCL000E#1#{60824b4c-eed1-4c9c-b49c-1b961461a819}\0001 ; Opens pin 1 with the leading 0s have no effect.

ファイル ハンドルを開くには、アプリケーションで Configuration Manager API (CM_Get_Device_Interface_*) を呼び出す必要があります。

ファイル ハンドルを開いた後、アプリケーションは DeviceIoControl 関数を呼び出すことによってこれらの要求を送信できます。 PWM セクションを参照してください。

ドライバーは、提供された PWM サポート ルーチン PwmParsePinPath を使用して、ピン パスを解析して検証し、ピン番号を抽出する必要があります。

デバイス インターフェイスのプロパティの設定

UWP アプリから PWM WinRT API を使用するには、これらの デバイス インターフェイスプロパティ を設定する必要があります。

  • DEVPKEY_DeviceInterface_Restricted

    現在の UWP デバイス アクセス モデルに従って、UWP アプリに PWM デバイス インターフェイスへのアクセスを許可するには、制限付きデバイス インターフェイス プロパティを FALSE に設定する必要があります。

  • DEVPKEY_DeviceInterface_SchematicName(リンク?)

    PwmController.GetDeviceSelector(FriendlyName) ファクトリ メソッドを使用するには、静的に接続された PWM デバイスの PWM デバイス インターフェイスに回路図名を割り当てる必要があります。 回路図名は、システム設計回路図 (PWM0、PWM_1など) で PWM デバイスに指定された名前です。 スケマティック名はシステム全体で一意であると見なされますが、適用されません。 少なくとも、同じ回路図名を持つ PWM デバイスが 2 つ存在してはいけません。それ以外の場合、WinRT PWM PwmController.GetDeviceSelector(FriendlyName) の動作は非決定論的になります。

プロパティは、次の 2 つの方法のいずれかで設定できます。

  1. PWM ドライバーの INF ファイルの使用

    AddProperty ディレクティブを使用して、デバイスのプロパティを設定します。 INF ファイルでは、PWM デバイス インスタンスの 1 つまたはサブセットで同じプロパティに異なる値を設定できるようにする必要があります。 DEVPKEY_DeviceInterface_Restricted設定の例を次に示します。

    ;***************************************** 
    ; Device interface installation 
    ;***************************************** 
    
    [PWM_Device.NT.Interfaces] 
    AddInterface={60824B4C-EED1-4C9C-B49C-1B961461A819},,PWM_Interface 
    
    [PWM_Interface] 
    AddProperty=PWM_Interface_AddProperty 
    
    ; Set DEVPKEY_DeviceInterface_Restricted property to false to allow UWP access 
    ; to the device interface without the need to be bound with device metadata. 
    ; If Restricted property is set to true, then only applications which are bound 
    ; with device metadata would be allowed access to the device interface. 
    
    [PWM_Interface_AddProperty] 
    {026e516e-b814-414b-83cd-856d6fef4822},6,0x11,,0 
    

    すべての設計に、PWM デバイスを UWP に公開するためのポリシーが同じとは言えません。 たとえば、ポリシーは、PWM デバイス インスタンスのサブセットへの UWP アクセスを許可する場合があります。 PWM デバイスのサブセットを公開するか、PWM デバイス インスタンスの 1 つまたは一部に異なるプロパティ値を割り当てるには、PWM デバイス インスタンスごとに異なるハードウェア ID を持ち、ポリシーに基づいて INF セクションを選択的に照合する必要があります。

    ACPI 割り当てハードウェア ID (_HID) がFSCL00E0され、一意の ID (_UID) が 0,...,3 である PWM0,..., PWM3 という名前の 4 つの同一 PWM デバイス インスタンス (IP ブロック) がある SoC ベースの設計を考えてみましょう。 すべての PWM デバイスを UWP に公開するには、ハードウェア ID ACPI\FSCL00E0で一致するようにDEVPKEY_DeviceInterface_Restrictedを設定する INF セクションが必要です。

    この方法でプロパティを設定する場合、ドライバー コードを変更する必要はありません。 これは、ドライバー バイナリよりも INF ファイルのサービスが簡単であるため、簡単なオプションです。 トレードオフは、このアプローチでは、各設計に合わせて調整された INF ファイルが必要であるという点です。

  2. PWM ドライバーでプログラムによって制御する

    PWM ドライバーは、IoSetDeviceInterfacePropertyData を呼び出して、PWM デバイス インターフェイスを作成して発行した後、EVT_WDF_DRIVER_DEVICE_ADD実装でデバイス インターフェイスプロパティを設定できます。 ドライバーは、割り当てる値とデバイスのプロパティを決定する役割を担います。 その情報は、通常、SoC ベースの設計のシステム ACPI に格納されます。 各デバイス インターフェイス プロパティの値は、各 ACPI デバイス ノード_DSDメソッドでデバイスのプロパティとして指定できます。 ドライバーは、ACPI からthe_DSDクエリを実行し、デバイスのプロパティ データを解析し、各プロパティの値を抽出して、デバイス インターフェイスを割り当てる必要があります。

    プロパティをプログラムで設定すると、ドライバーとその INF ファイルが設計間で移植可能になります。そのため、BSP では、各 PWM デバイス ノードを定義する ACPI DSDT のみが変更されます。 ただし、ACPI バイナリ ブロックの読み取りと解析は面倒であり、多くのコードが必要です。多くのコードが必要であり、エラーや脆弱性が発生しやすくなって、より大きなエラーサーフェスが発生する可能性があります。

ファイルの開く/閉じるイベントの処理

PWM ドライバーは、EVT_WDF_DEVICE_FILE_CREATEおよびEVT_WDF_FILE_CLEANUP/EVT_WDF_FILE_CLOSEコールバック関数を実装することによって、ファイル作成および閉じるイベントに登録する必要があります。 実装では、ドライバーは次のタスクを実行する必要があります。

  1. 作成要求がコントローラー用かピン用かを判断します。

  2. 要求がピンの場合、ドライバーは、pin 要求を作成するためのピン パスを解析して検証し、要求されたピン番号がコントローラーの境界内にあることを確認する必要があります。

  3. コントローラーとピンの作成要求へのアクセスを許可または拒否します。

  4. 明確に定義されたステート マシンごとにコントローラーとピンの状態整合性を維持します。

上記の一連のタスクでは、次のように、EVT_WDF_DEVICE_FILE_CREATEで検証の 2 番目のタスクを実行できます。

  1. 要求ファイル オブジェクトに関連付けられているファイル名が null の場合は、STATUS_INVALID_DEVICE_REQUESTを使用して要求を完了します。

  2. 要求ファイル オブジェクトに関連付けられているファイル名が空の文字列の場合、これはコントローラーの作成要求です。それ以外の場合は pin 作成要求です。

  3. これがピン作成要求の場合は、次のようになります。

    1. 形式 <DecimalName> に基づいてピン パスを解析し、PwmParsePinPath を呼び出してピン番号を抽出します。

    2. ピン パスの解析と検証に失敗した場合は、STATUS_NO_SUCH_FILEを使用して要求を完了します。

    3. ピン番号がコントローラーのピン数以上の場合は、STATUS_NO_SUCH_FILEで要求を完了します。 ピン番号は 0 から始まるインデックスであることに注意してください。

    4. それ以外の場合は、EVT_WDF_DEVICE_FILE_CREATEの処理を続行します。

EVT_WDF_DEVICE_FILE_CREATE ハンドラーに対して前述の検証手順を実装するサンプル コードを次に示します。

EVT_WDF_DEVICE_FILE_CREATE PwmEvtDeviceFileCreate;

VOID 

PwmEvtDeviceFileCreate ( 
    WDFDEVICE WdfDevice, 
    WDFREQUEST WdfRequest, 
    WDFFILEOBJECT WdfFileObject 
    ) 
{ 

    UNICODE_STRING* filenamePtr = WdfFileObjectGetFileName(WdfFileObject); 
    IMXPWM_DEVICE_CONTEXT* deviceContextPtr = PwmGetDeviceContext(WdfDevice); 
    NTSTATUS status; 
    ULONG pinNumber; 

    // 
    // Parse and validate the filename associated with the file object 
    // 

    bool isPinInterface; 

    if (filenamePtr == nullptr) { 

        WdfRequestComplete(WdfRequest, STATUS_INVALID_DEVICE_REQUEST); 

        return; 

    } else if (filenamePtr->Length > 0) { 

        // 
        // A non-empty filename means to open a pin under the controller namespace 
        // 

        status = PwmParsePinPath(filenamePtr, &pinNumber); 

        if (!NT_SUCCESS(status)) { 

            WdfRequestComplete(WdfRequest, status); 

            return; 

        } 


        if (pinNumber >= deviceContextPtr->ControllerInfo.PinCount) { 

            WdfRequestComplete(WdfRequest, STATUS_NO_SUCH_FILE); 

            return; 

        } 


        isPinInterface = true; 

    } else { 

        // 
        // An empty filename means that the create is against the root controller 
        // 

        isPinInterface = false; 
    } 

    // 
    // Continue request processing here 
    // 
} 

コントローラーとピンの共有

コントローラーとピンの共有モデルは、複数のリーダーと単一のライター パターンに従います。 コントローラー/ピンは複数の呼び出し元が読み取り用に開くことができますが、そのコントローラー/ピンを一度に開くことができるのは 1 つの呼び出し元だけです。

このモデルは、ファイル ハンドルを開くときに Desired Access フラグと Share Access フラグの組み合わせを使用して実装できます。 DDI 共有モデルでは、アクセスの制御に Desired Access 仕様のみが使用される、より単純な共有セマンティクスを選択します。 共有アクセスの仕様は、共有モデルでは何の役割も果たせず、コントローラーまたはピンを開くときに指定した場合は適用されません。

EVT_WDF_DEVICE_FILE_CREATEでは、コントローラー/ピンの状態に基づいて、または次のように、作成要求 Desired Access と Share Access を抽出して検証する必要があります。

  1. Share Access が 0 でない場合は、アクセスを拒否し、STATUS_SHARING_VIOLATIONで要求を完了します。

  2. Desired Access が読み取り専用の場合は、アクセス権を付与し、EVT_WDF_DEVICE_FILE_CREATEの処理を続行します。

  3. 必要なアクセスが書き込み用の場合は、次のようになります。

コントローラー/ピンが既に書き込み用に開かれている場合は、アクセスを拒否し、STATUS_SHARING_VIOLATIONで要求を完了します。それ以外の場合は、コントローラーまたはピンを書き込み用に開いているものとしてマークし、EVT_WDF_DEVICE_FILE_CREATE処理を続行します。

作成要求から Desired Access と Share Access を抽出する方法の例を次に示します。

void 
PwmCreateRequestGetAccess( 
    _In_ WDFREQUEST WdfRequest, 
    _Out_ ACCESS_MASK* DesiredAccessPtr, 
    _Out_ ULONG* ShareAccessPtr 
    ) 
{ 

    NT_ASSERT(ARGUMENT_PRESENT(DesiredAccessPtr)); 

    NT_ASSERT(ARGUMENT_PRESENT(ShareAccessPtr)); 


    WDF_REQUEST_PARAMETERS wdfRequestParameters; 

    WDF_REQUEST_PARAMETERS_INIT(&wdfRequestParameters); 

    WdfRequestGetParameters(WdfRequest, &wdfRequestParameters); 


    NT_ASSERTMSG( 

        "Expected create request", 
        wdfRequestParameters.Type == WdfRequestTypeCreate); 


    *DesiredAccessPtr = 
        wdfRequestParameters.Parameters.Create.SecurityContext->DesiredAccess; 

    *ShareAccessPtr = wdfRequestParameters.Parameters.Create.ShareAccess; 
} 

コントローラーとピンの独立性

コントローラーとピンには親子関係があります。 ピンを開くには、まずその親コントローラーを開く必要があります。 もう 1 つの方法は、ピンを独立したエンティティとして見て、親コントローラーがピンに 1 つのサービスのみを提供し、そのコントローラーに含まれるすべてのピンのグローバル PWM 期間を設定することです。

シナリオの例をいくつか示します。

  • 単一プロセスアクセス

    • プロセス A は、ピンを開き、そのデューティ サイクルを設定し、コントローラーの既定の期間を使用して開始し、後でコントローラーを開き、オンデマンドでその期間を設定できます。 既定の期間がアプリケーションに対して OK の場合、コントローラーを開く必要がない場合があります。

    • プロセスには複数のスレッドがあり、それぞれが同じコントローラーの下で異なるピンを制御します。

  • マルチプロセス アクセス:

    • コマンド ライン ユーティリティは、コンソールに情報を表示するために、読み取り専用アクセスでコントローラーを開くことができます。 同時に、バックグラウンド UWP タスクは書き込み用にコントローラーを開き、1 つのピンで一部の LED を制御します。

    • PWM期間の書き込みとロックのためにコントローラを保持しながら、Pin0を介して液晶バックライトを制御するカーネルモードディスプレイドライバ。 同時に、Win32 サービスはディスプレイ ドライバーによって設定された PWM 期間を使用し、Pin1 を使用して LED を暗くしてユーザーに何らかの状態を伝えます。

コントローラーをピンから独立して開いたり閉じたりすることには、いくつかの重要な影響があることに注意してください。 詳細については、ステート マシンのセクションを参照してください。

コントローラーおよびピンステート マシン

コントローラーの状態の定義

状態機能 既定値 説明
Is-Opened-For-Write いいえ False は、コントローラーが閉じているか、読み取りのために開かれていることを示します。True は、書き込み用に開かれていることを示します。
Desired-Period 最小期間

コントローラーステートマシン。

以下のコントローラー ステート マシンは、Is-Opened-For-Write 状態のみを中心にしています。 必要な期間の値も、コントローラーで実行できる操作の種類に影響しないため、省略されます。 書き込み用に開かれたコントローラーが、書き込み用に開いた呼び出し元によって閉じられるたびに、コントローラーは既定値 (既定の目的の期間) にリセットされることに注意してください。

ピン留め状態の定義

状態機能 既定値 説明
開かれた状態 -For-Write いいえ False は、ピンが閉じているか、読み取りのために開かれていることを示します。True は、書き込み用に開かれていることを示します。
アクティブ-Duty-Cycle 0
Is-Started いいえ False は停止を示します。True は開始を示します。

ステート マシンをピン留めします。

ピン ステート マシンは、2 つの状態の Is-Opened-For-Write と Is-Started の組み合わせを中心にしています。 極性やアクティブ デューティ サイクルなどの他のピンの状態は、その値がピンで実行できる操作の種類に影響しないため、除外されます。 書き込み用に開かれたピンは、同じ呼び出し元によって閉じられるたびに、そのピンはデフォルト (停止、デフォルトの極性、およびアクティブデューティーサイクル) に戻ります。 また、Is-Started = true の状態での Set-Polarity 遷移は、その状態では無効であるため、除外されることに注意してください。

特定の状態に関して言及されていない遷移は、このような遷移が無効であるか不可能であり、対応する要求を適切なエラー状態で完了する必要があることを意味します。

状態遷移の実装に関する考慮事項

  • EVT_WDF_DEVICE_FILE_CREATE では、ドライバーは、作成要求における望ましいアクセスおよび次のようなコントローラーまたはピンの Is-Opened-For-Write 状態に基づいてアクセスを許可または拒否する必要があります。

    要求に必要な書き込みアクセス権があり、コントローラー/ピンが既に書き込み用に開かれている場合は、STATUS_SHARING_VIOLATIONで要求を完了します。それ以外の場合は、コントローラー/ピンを書き込み用に開いているものとしてマークします (Is-Opened-For-Write = true)、アクセス権を付与し、処理を続行します。

    この例では、コンカレント ファイル作成要求を処理するために必要なロック ロジックが省略されているEVT_WDF_DEVICE_FILE_CREATE ハンドラーに対して、前述のアクセス検証手順を実装します。

    //
    // Verify request desired access
    //
    
    const bool hasWriteAccess = desiredAccess & FILE_WRITE_DATA;
    
    if (isPinInterface) {
        PWM_PIN_STATE* pinPtr = deviceContextPtr->Pins + pinNumber;
        if (hasWriteAccess) {
            if (pinPtr->IsOpenForReadWrite) {
                PWM_LOG_TRACE("Pin%lu access denied.", pinNumber);
                WdfRequestComplete(WdfRequest, STATUS_SHARING_VIOLATION);
                return;
            }
            pinPtr->IsOpenForReadWrite = true;
        }
        PWM_LOG_TRACE(
            "Pin%lu Opened. (IsOpenForReadWrite = %lu)",
            pinNumber,
            (pinPtr->IsOpenForReadWrite ? 1 : 0));
    
    } else {
        if (hasWriteAccess) {
            if (deviceContextPtr->IsControllerOpenForReadWrite) {
                PWM_LOG_TRACE("Controller access denied.");
                WdfRequestComplete(WdfRequest, STATUS_SHARING_VIOLATION);
                return;
            }
            deviceContextPtr->IsControllerOpenForReadWrite = true;
        }
        PWM_LOG_TRACE(
            "Controller Opened. (IsControllerOpenForReadWrite = %lu)",
            (deviceContextPtr->IsControllerOpenForReadWrite ? 1 : 0));
    }
    
    //
    // Allocate and fill a file object context
    //
    IMXPWM_FILE_OBJECT_CONTEXT* fileObjectContextPtr;
    {
        WDF_OBJECT_ATTRIBUTES wdfObjectAttributes;
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
            &wdfObjectAttributes,
            IMXPWM_FILE_OBJECT_CONTEXT);
    
        void* contextPtr;
        NTSTATUS status = WdfObjectAllocateContext(
                WdfFileObject,
                &wdfObjectAttributes,
                &contextPtr);
        if (!NT_SUCCESS(status)) {
            IMXPWM_LOG_ERROR(
                "WdfObjectAllocateContext(...) failed. (status = %!STATUS!)",
                status);
            WdfRequestComplete(WdfRequest, status);
            return;
        }
    
        fileObjectContextPtr =
            static_cast<IMXPWM_FILE_OBJECT_CONTEXT*>(contextPtr);
    
        NT_ASSERT(fileObjectContextPtr != nullptr);
        fileObjectContextPtr->IsPinInterface = isPinInterface;
        fileObjectContextPtr->IsOpenForWrite = hasWriteAccess;
        fileObjectContextPtr->PinNumber = pinNumber;
    }
    
  • EVT_WDF_FILE_CLOSE/EVT_WDF_FILE_CLEANUPでは、ドライバーはコントローラー/ピンの状態の整合性を維持する必要があります。

    ファイル オブジェクトがそのコントローラー/ピンを書き込み用に開いたコントローラー/ピンに属している場合は、コントローラー/ピンを既定の状態にリセットし、そのコントローラー/ピンが書き込み用に開かれていないことをマーク解除します (Is-Opened-For-Write = false)。

    この例では、同時実行ファイルクローズ要求を処理するために必要なロック ロジックが省略されているEVT_WDF_DEVICE_FILE_CLOSE ハンドラーに対して、前述のアクセス検証手順を実装します。

    EVT_WDF_DEVICE_FILE_CLOSE PwmEvtFileClose;
    
    VOID
    PwmEvtFileClose (
        WDFFILEOBJECT WdfFileObject
        )
    {
        WDFDEVICE wdfDevice = WdfFileObjectGetDevice(WdfFileObject);
        PWM_DEVICE_CONTEXT* deviceContextPtr = PwmGetDeviceContext(wdfDevice);
        PWM_FILE_OBJECT_CONTEXT* fileObjectContextPtr = PwmGetFileObjectContext(WdfFileObject);
    
        if (fileObjectContextPtr->IsPinInterface) {
            if (fileObjectContextPtr->IsOpenForReadWrite) {
                const ULONG pinNumber = fileObjectContextPtr->PinNumber;
    
                NTSTATUS status = PwmResetPinDefaults(deviceContextPtr, pinNumber);
                if (!NT_SUCCESS(status)) {
                    PWM_LOG_ERROR(
                        "PwmResetPinDefaults(...) failed. "
                        "(pinNumber = %lu, status = %!STATUS!)",
                        pinNumber,
                        status);
                    //
                    // HW Error Recovery
                    //
                }
    
                NT_ASSERT(deviceContextPtr->Pins[pinNumber].IsOpenForReadWrite);
                deviceContextPtr->Pins[pinNumber].IsOpenForReadWrite = false;
            }
    
            PWM_LOG_TRACE("Pin%lu Closed.", fileObjectContextPtr->PinNumber);
    
        } else {
            if (fileObjectContextPtr->IsOpenForReadWrite) {
                NTSTATUS status = PwmResetControllerDefaults(deviceContextPtr);
                if (!NT_SUCCESS(status)) {
                    IMXPWM_LOG_ERROR(
                        "PwmResetControllerDefaults(...) failed. (status = %!STATUS!)",
                        status);
                    //
                    // HW Error Recovery
                    //  
                }
    
                NT_ASSERT(deviceContextPtr->IsControllerOpenForReadWrite);
                deviceContextPtr->IsControllerOpenForReadWrite = false;
            }
    
            PWM_LOG_TRACE("Controller Closed.");
        }
    }
    

PWM IOCTL 要求

PWM IOCTL 要求は、アプリケーションまたは別のドライバーによって送信され、コントローラーまたは特定のピンを対象とします。

コントローラー IOCTL

IOCTL をピン留めする

IOCTL 要求ごとに、PWM ドライバーは次のことを確認する必要があります。

  1. 要求された操作 (IOCTL コード) は、要求に関連付けられているファイル オブジェクトに対して有効です。

  2. 入力バッファーと出力バッファーを要求し、少なくとも予想される最小サイズであることを確認します。

  3. 現在のコントローラー/ピン状態での要求された操作の有効性。

  4. 個々の入力パラメーターの有効性。 たとえば、必要な 0 の期間は、IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIODの無効なパラメーターです。

IOCTL 完了状態コード

PWM ドライバーは、適切な状態コードを使用して IOCTL 要求を完了する必要があります。 一般的な完了状態コードを次に示します。 一般に、既に設定されている値を持つプロパティを設定する IOCTL は常に成功する必要があります。 Foe の例では、既に設定されているのとまったく同じ期間を設定したり、既に停止しているピンを停止したり、既に設定されている極性を設定したりします。

STATUS_NOT_SUPPORTED

要求された IOCTL 操作は実装もサポートもされていません。 たとえば、一部のコントローラーでは、出力信号の極性の設定をサポートしていない場合があります。このような場合、IOCTL_PWM_PIN_SET_POLARITYを実装する必要がありますが、既定以外の極性の場合はSTATUS_NOT_SUPPORTEDで失敗します。

STATUS_INVALID_DEVICE_REQUEST

IOCTL 要求が間違ったターゲットに送信されました。 たとえば、コントローラー IOCTL 要求は、ピン ファイル ハンドルを使用して送信されました。

STATUS_BUFFER_TOO_SMALL

入力または出力のバッファー サイズが、要求を処理するために必要な最小バッファー サイズより小さい。 WdfRequestRetrieveInputBuffer または WdfRequestRetrieveOutputBuffer を使用して入力バッファーと出力バッファーを取得および検証する WDF ドライバーは、対応するエラー状態をそのまま返すことができます。 入力バッファーまたは出力バッファーが定義されているすべての IOCTL には、そのバッファーを記述する対応する構造体があり、入力構造体と出力構造体名はそれぞれ INPUT と _OUTPUT後置を持ちます。入力バッファーの最小サイズは sizeof (PWMINPUT) ですが、出力バッファーの最小サイズは sizeof(PWM_OUTPUT) です。

IOCTL コード 説明
IOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIOD パルス幅変調(PWM)コントローラの出力チャンネルで測定される有効な出力信号期間を取得します。 PWM_CONTROLLER_GET_ACTUAL_PERIOD_OUTPUT値を返します。 Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。
  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_GET_INFO (PWMコントローラー情報取得) パルス幅変調 (PWM) コントローラーに関する情報を取得します。 この情報は、コントローラーの初期化後は変更されません。

呼び出し元は、PWM_CONTROLLER_INFO構造体の正確なサイズを持つ出力バッファーを渡す必要があります。 ドライバーは、要求出力バッファー サイズから構造体のバージョンを推論します。

バッファー サイズが最小の構造体バージョンのサイズより小さい場合は、IOCTL 完了状態の STATUS_BUFFER_TOO_SMALL を使用して要求が完了します。 それ以外の場合、ドライバーは、指定された出力バッファーに収めることができる最高の構造バージョンを想定し、要求を正常に完了します。

新しいPWM_CONTROLLER_INFO バージョンのバイト サイズが以前のバージョンよりも大きい

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。
  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_CONTROLLER_SET_DESIRED_PERIOD パルス幅変調(PWM)コントローラの出力信号期間を推奨値に設定します。

PWM コントローラは、その機能に基づいて、要求された値にできるだけ近い期間を設定しようとします。 有効期間は IOCTL 出力として返されます。 後でIOCTL_PWM_CONTROLLER_GET_ACTUAL_PERIODを使用して取得できます。

必要な期間は 0 より大きく、コントローラーでサポートされている期間の範囲内である必要があります。 つまり、minimumPeriod と MaximumPeriod の範囲内である必要があります。これは、IOCTL_PWM_CONTROLLER_GET_INFOを使用して取得できます。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。
  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • ステータス_無効なパラメータ
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_アクティブデューティサイクルパーセンテージ取得 ピンまたはチャネルの現在のデューティ サイクルの割合を取得します。 コントロール コードは、パーセンテージをPWM_PIN_GET_ACTIVE_DUTY_CYCLE_PERCENTAGE_OUTPUT構造体として返します。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。

  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PINにおけるアクティブデューティサイクルの割合設定 コントローラピンまたはチャンネルに必要なデューティサイクルパーセンテージ値を設定します。 コントロール コードは、パーセンテージをPWM_PIN_SET_ACTIVE_DUTY_CYCLE_PERCENTAGE_INPUT構造体として指定します。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。

  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_GET_POLARITY ピンまたはチャネルの現在の信号極性を取得します。 制御コードは、信号極性をPWM_PIN_GET_POLARITY_OUTPUT構造として取得します。 信号極性は、PWM_POLARITY列挙体で定義されているように、アクティブハイまたはアクティブローのいずれかです。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。

  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_SET_POLARITY ピンまたはチャンネルの信号極性を設定します。 制御コードは、PWM_PIN_SET_POLARITY_INPUT構造に基づいて信号極性を設定します。 シグナル極性は、PWM_POLARITY列挙型で定義されているように、アクティブハイまたはアクティブローのいずれかです。

極性の変更は、ピンが停止した場合にのみ許可されます。 IOCTL_PWM_PIN_IS_STARTEDコントロール コードを使用して、ピンが停止しているかどうかを確認できます。 ピンが停止し、要求された極性が現在のピン極性と異なる場合、要求はSTATUS_INVALID_DEVICE_STATE値で完了します。

ピンの起動時に極性を変更すると、一部のパルス幅変調(PWM)コントローラでグリッチが発生する可能性があります。 極性を変更する場合は、最初にピンを停止し、極性を変更してから、ピンを起動します。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。

  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • ステータス_無効なパラメータ
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_START ピンまたはチャンネルでパルス幅変調(PWM)信号の生成を開始します。 ピンが開始されているかどうかを確認するには、IOCTL_PWM_PIN_IS_STARTEDを使用します。

既に開始されているピンまたはチャネルでこの IOCTL を発行しても効果はありませんが、成功します。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。

>
  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_STOP ピンまたはチャネルでのパルス幅変調 (PWM) 信号の生成を停止します。 ピンが開始されているかどうかを確認するには、IOCTL_PWM_PIN_IS_STARTEDを使用します。

既に停止しているピンまたはチャネルでこの IOCTL を発行しても効果はありませんが、成功します。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。

  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL
IOCTL_PWM_PIN_IS_STARTED ピンまたはチャネルの信号生成の状態を取得します。 各ピンには、PWM_PIN_IS_STARTED_OUTPUT構造として開始または停止の状態があります。 開始状態のブール値は true です。 停止状態は false です。

既定では、ピンは開くと停止し、閉じたときや解放されたときに停止状態に戻ります。

Irp->IoStatus.Status は、次の一覧のいずれかの値に設定されます。

  • ステータス_成功
  • STATUS_NOT_SUPPORTED
  • STATUS_INVALID_DEVICE_REQUEST
  • STATUS_BUFFER_TOO_SMALL