Skip to content

ドライバ開発情報

開発20 toaster\busのロードとドライバエントリの解析

デバイスドライバをはじめ、デバイスにかかわるお困りごとの際はお気軽にお問い合わせください。


Driverディレクトリ dispatch.cの解析

オープンクローズルーチン

NTSTATUS XxDispatchOpenClose(
    IN PDEVICE_OBJECT pDO,
    IN PIRP Irp
)
{
    Irp->IoStatus.Status = STATUS_SUCCESS;             // IRPの完了状態は成功
    Irp->IoStatus.Information = 0;                     // 読み書きされたバイト長は0
    IoCompleteRequest(Irp, IO_NO_INCREMENT);          // IRPの完了を伝える
    return STATUS_SUCCESS;
}

 

書き込みルーチン

NTSTATUS XxDispatchWrite(
    IN PDEVICE_OBJECT pDO,
    IN PIRP Irp
)
{
    PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp); // 現在のスタックロケーションを取得
    // ディスパッチルーチン、他のルーチンのポインタを準備します
    if (IrpStack->Parameters.Write.Length == 0) {
        Irp->IoStatus.Status = STATUS_SUCCESS;             // IRPの完了状態は成功
        Irp->IoStatus.Information = 0;                     // 読み書きされたバイト長は0
        IoCompleteRequest(Irp, IO_NO_INCREMENT); // IRPの完了を伝える
        return STATUS_SUCCESS;
    }

    IoMarkIrpPending(Irp);                 // 0でない時、IRPをペンディングとして印をつける
    IoStartPacket(pDO, Irp, 0, NULL); // StartI/OのキューにIRPを挿入
                                        // StartI/Oルーチンが起動していない場合、起動させる
    return STATUS_PENDING;
}


Testディレクトリ xxtest.cの解析

VOID __cdecl main(VOID)
{
    HANDLE hDevice;       // CreateFileによって取得したファイルのハンドル
    DWORD dwBytesWritten; // デバイスに書き込まれたデータのバイト数
    LPCTSTR szBuffer = "The owls are not what they seem.\n\0";
    DWORD dwErrorCode;    // エラーコード値

    // デバイスオブジェクトを作成します
    hDevice = CreateFile(
        "\\\\.\\XX1",             // ファイル名のポインタ
        GENERIC_WRITE,            // デバイスにデータを書き込むことができる
        0,                        // オブジェクトは共有しない
        NULL,                     // 取得したハンドルは子プロセスに継承できない
        OPEN_EXISTING,            // 指定したファイルを開く。
                                  // ファイルが存在しない場合、関数は失敗する。
        FILE_ATTRIBUTE_NORMAL,    // ファイルの属性は設定しない
        NULL
    );

    if (hDevice == INVALID_HANDLE_VALUE) { // CreateFileの実行に失敗したとき
        dwErrorCode = GetLastError();      // 最新のエラーコードの取得
        ErrorMessage("CreateFile", dwErrorCode); // エラーメッセージの表示
        ExitProcess(dwErrorCode);          // プロセスを終了
    }

    if (!WriteFile(
        hDevice,       // 書き込みを行うファイルハンドル
        szBuffer,      // データバッファのポインタ
        strlen(szBuffer), // データのバイト数
        &dwBytesWritten, // 実際に書き込まれたデータのバイト数の変数アドレス
        NULL           // オーバーラップI/O用の構造体ポインタ
    )) { // WriteFileの実行に失敗したとき
        dwErrorCode = GetLastError();      // 最新のエラーコードの取得
        ErrorMessage("WriteFile", dwErrorCode); // エラーメッセージの表示
        ExitProcess(dwErrorCode);          // プロセスを終了
    }

    printf("Success ! %d bytes written.\n", dwBytesWritten); // WriteFileに成功した際メッセージを表示させる
    CloseHandle(hDevice);     // ファイルハンドルを閉じる
    ExitProcess(ERROR_SUCCESS); // プロセスを終了
}


エラーメッセージルーチン

#include <windows.h>
#include <stdio.h>
// デバイスエクステンション構造体を busenum.h に定義しています。(FDO_DEVICE_DATA)

static VOID ErrorMessage(
    LPTSTR lpOrigin,
    DWORD dwMessageId
);

static VOID ErrorMessage(
    // デバイス名はありません。従って、シンボリックリンクしません。
    LPTSTR lpOrigin,       // エラーの場所を示す
    DWORD dwMessageId        // ERROR_XXXコード値
)
{
    LPTSTR msgBuffer;      // システムから返された文字列
    DWORD cBytes;         // 返された文字列長

    cBytes = FormatMessage(            // I/Oマネージャによって1度に1個のリクエストしか受けられません。
        FORMAT_MESSAGE_FROM_SYSTEM |         // システムメッセージテーブルリソースから検索するよう指定
        FORMAT_MESSAGE_ALLOCATE_BUFFER,          // バッファの割り当てを要求
        NULL,                 // メッセージ定義の位置を指定
        dwMessageId,               // メッセージ識別子を指定
        MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),          // メッセージの言語識別子を指定
        (LPTSTR)&msgBuffer,           // バッファへのポインタを指定
        500,                // 出力バッファに格納できる最大文字数
        NULL
    );

    if (msgBuffer) {
        msgBuffer[cBytes] = TEXT('\0');
        printf("Error: %s -- %s\n", lpOrigin, msgBuffer);
        LocalFree(msgBuffer);        // ローカルメモリオブジェクトを解放し、ハンドルを無効にする
    } else {
        printf("FormatMessage error: %d\n", GetLastError());
    }
}

 

 

「toaster\bus」の解析

Bus_FDO_Powerルーチン(power.c)

NTSTATUS Bus_FDO_Power(
    PFDO_DEVICE_DATA Data,
    PIRP Irp // デバイスエクステンションにデータをセットする
)
{
    NTSTATUS status;       // 完了ステータス
    POWER_STATE powerState;
    POWER_STATE_TYPE powerType;
    PIO_STACK_LOCATION stack; // スタックロケーションのポインタ

    stack = IoGetCurrentIrpStackLocation(Irp); // 現在のスタックロケーションを取得
    powerType = stack->Parameters.Power.Type;    // 要求の種類を示す
    powerState = stack->Parameters.Power.State;  
    // Errorが呼ばれるとデバイススタックからデバイスをデタッチします。また、デバイスオブジェクトを削除します。

    if (Data->DevicePnPState == NotStarted) {
        PoStartNextPowerIrp(Irp);               // 次のパワーIRP開始の準備をする
        IoSkipCurrentIrpStackLocation(Irp);     // 現在のスタックロケーションの内容を
                                                // 次に低い階層のスタックロケーションにコピーする
        return PoCallDriver(Data->NextLowerDriver, Irp); // 次に低い階層のスタックロケーションにパワーIRPを受け渡す
    }

    Bus_IncIoCount(Data); // デバイスの受け取った要求回数を1増やす

    if (stack->MinorFunction == IRP_MN_SET_POWER) {
    // PnPのマイナー機能[IRP_MN_START_DEVICE] デバイス起動時に呼ばれます。
        Bus_KdPrint_Cont(Data, BUS_DBG_POWER_TRACE,
            ("\tRequest to set %s state to %s\n",
            ((powerType == SystemPowerState) ? "System" : "Device"),
            ((powerType == SystemPowerState) ?
                DbgSystemPowerString(powerState.SystemState) :
                DbgDevicePowerString(powerState.DeviceState))));
    }

    PoStartNextPowerIrp(Irp);           // 次のパワーIRP開始の準備をする
    IoSkipCurrentIrpStackLocation(Irp); // 現在のスタックロケーションの内容を次に
                                        // 低い階層のスタックロケーションにコピーする
    status = PoCallDriver(Data->NextLowerDriver, Irp); // 次に低い階層のスタックロケーションにパワーIRPを受け渡す
    Bus_DecIoCount(Data); // デバイスの受け取った要求回数を1減らす

    return status;
}
 

Bus_IoCtlルーチン(busenum.c)

NTSTATUS Bus_IoCtl(
    // サブルーチン Bus_StartFdo を呼びます。具体的なスタート処理はこのサブルーチンで処理します。
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
)
{
    PIO_STACK_LOCATION irpStack; // スタックロケーションのポインタ
    NTSTATUS status;             // 完了ステータス
    ULONG inlen, outlen;         // バイト長
    PFDO_DEVICE_DATA fdoData;    // デバイスエクステンションのポインタ
    PVOID buffer;                // バッファのポインタ

    PAGED_CODE(); // このルーチンはページプールメモリで実行します

    fdoData = (PFDO_DEVICE_DATA)DeviceObject->DeviceExtension;
    // デバイスオブジェクトからデバイスエクステンションのポインタの取得
    // デバイスの停止保留状態から、起動状態に戻します。
    if (!fdoData->IsFDO) {
        status = STATUS_INVALID_DEVICE_REQUEST; // IRPの完了状態はデバイスが無効
        Irp->IoStatus.Status = status;         // IRPに状態を設定
        IoCompleteRequest(Irp, IO_NO_INCREMENT); // IRPの完了を伝える
        return status;
    }
    // デバイスの停止処理を行います。
    if (fdoData->DevicePnPState == Deleted) {
        Irp->IoStatus.Status = status = STATUS_DELETE_PENDING; // 状態の設定
        IoCompleteRequest(Irp, IO_NO_INCREMENT); // IRPの完了
        return status;
    }

    Bus_IncIoCount(fdoData);                     // デバイスの受け取った要求回数を1増やす
    irpStack = IoGetCurrentIrpStackLocation(Irp); // 現在のスタックロケーションを取得
    buffer = Irp->AssociatedIrp.SystemBuffer;    // システムバッファの取得
    inlen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
    // 入力バッファのバイト長を取得
    outlen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    // 出力バッファのバイト長を取得
    status = STATUS_INVALID_PARAMETER; // 定義していないコントロールコード

    switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { // デバイスを削除できるか調べます。
        // デバイスの削除保留状態から起動状態に戻します。
        case IOCTL_BUSENUM_PLUGIN_HARDWARE:
            if ((inlen == outlen) &&
                ((sizeof(BUSENUM_PLUGIN_HARDWARE) + sizeof(UNICODE_NULL) * 2) <= inlen) &&
                (sizeof(BUSENUM_PLUGIN_HARDWARE) == ((PBUSENUM_PLUGIN_HARDWARE)buffer)->Size)) {
                Bus_KdPrint(fdoData, BUS_DBG_IOCTL_TRACE, ("PlugIn called\n")); // 完了状態を設定
                status = Bus_PlugInDevice((PBUSENUM_PLUGIN_HARDWARE)buffer, inlen, fdoData); // メッセージの表示
                Irp->IoStatus.Information = outlen; // 読み書きしたバイト長の設定
            }
            break;

        case IOCTL_BUSENUM_UNPLUG_HARDWARE:
            if ((sizeof(BUSENUM_UNPLUG_HARDWARE) == inlen) &&
                (inlen == outlen) &&
                (((PBUSENUM_UNPLUG_HARDWARE)buffer)->Size == inlen)) {
                Bus_KdPrint(fdoData, BUS_DBG_IOCTL_TRACE, ("UnPlug called\n")); // メッセージの表示
                status = Bus_UnPlugDevice((PBUSENUM_UNPLUG_HARDWARE)buffer, fdoData); // 完了状態を設定
                Irp->IoStatus.Information = outlen; // 読み書きしたバイト長の設定
            }
            break;

        case IOCTL_BUSENUM_EJECT_HARDWARE:
            if ((sizeof(BUSENUM_EJECT_HARDWARE) == inlen) &&
                (inlen == outlen) &&
                (((PBUSENUM_EJECT_HARDWARE)buffer)->Size == inlen)) {
                Bus_KdPrint(fdoData, BUS_DBG_IOCTL_TRACE, ("Eject called\n")); // メッセージの表示
                status = Bus_EjectDevice((PBUSENUM_EJECT_HARDWARE)buffer, fdoData); // 完了状態を設定
                Irp->IoStatus.Information = outlen; // 読み書きしたバイト長の設定
            }
            break;

        default:
            break; // 突然のデバイス削除が発生した時に呼び出されます。
    }

    Irp->IoStatus.Status = status;         // 完了状態を設定
    IoCompleteRequest(Irp, IO_NO_INCREMENT); // IRPの完了
    Bus_DecIoCount(fdoData);               // デバイスの受け取った要求回数を1減少させる

    return status;
}

 

参考資料

「WDMデバイスドライバプログラミング完全ガイド」下
Edward N. Dekker, Joseph M. Newcomer著 株式会社クイック訳
「Windows NTデバイスドライバプログラミング」
アート・ベーカー著 WinProgDDK ML和訳プロジェクト訳