JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
デバイスドライバの記述     Oracle Solaris 10 8/11 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

パート I Solaris プラットフォーム用デバイスドライバの設計

1.  Solaris デバイスドライバの概要

2.  Solaris カーネルとデバイスツリー

3.  マルチスレッド

4.  プロパティー

5.  イベントの管理とタスクのキュー

6.  ドライバの自動設定

7.  デバイスアクセス: プログラム式入出力

8.  割り込みハンドラ

9.  ダイレクトメモリーアクセス (DMA)

10.  デバイスメモリーおよびカーネルメモリーのマッピング

11.  デバイスコンテキスト管理

12.  電源管理

13.  Solaris ドライバの強化

14.  階層化ドライバインタフェース (LDI)

パート II 特定の種類のデバイスドライバの設計

15.  文字デバイスのドライバ

文字ドライバの構造の概要

文字デバイスの自動設定

デバイスアクセス (文字ドライバ)

open() エントリポイント (文字ドライバ)

close() エントリポイント (文字ドライバ)

入出力要求の処理

ユーザーアドレス

ベクトル化された入出力

同期入出力と非同期入出力の違い

データ転送方法

プログラム式入出力転送

DMA 転送 (同期)

DMA 転送 (非同期)

minphys() エントリポイント

strategy() エントリポイント

デバイスメモリーのマッピング

ファイル記述子に対する入出力の多重化

その他の入出力制御

ioctl() エントリポイント (文字ドライバ)

64 ビットに対応したデバイスドライバに対する入出力制御のサポート

copyout() のオーバーフローの処理

32 ビットと 64 ビットのデータ構造体マクロ

構造体マクロの動作のしくみ

構造体マクロを使用する場合

構造体ハンドルの宣言と初期化

構造体ハンドルに対する操作

その他の操作

16.  ブロックデバイスのドライバ

17.  SCSI ターゲットドライバ

18.  SCSI ホストバスアダプタドライバ

19.  ネットワークデバイスのドライバ

20.  USB ドライバ

パート III デバイスドライバの構築

21.  ドライバのコンパイル、ロード、パッケージ化、およびテスト

22.  デバイスドライバのデバッグ、テスト、およびチューニング

23.  推奨されるコーティング方法

パート IV 付録

A.  ハードウェアの概要

B.  Solaris DDI/DKI サービスの概要

C.  64 ビットデバイスドライバの準備

D.  コンソールフレームバッファードライバ

索引

その他の入出力制御

ioctl(9E) ルーチンは、ユーザースレッドが、デバイスに関連付けられたファイル記述子に対する ioctl(2) システムコールを発行したときに呼び出されます。入出力制御メカニズムとは、デバイス固有のパラメータを取得したり、設定したりするためのものです。このメカニズムは、内部のドライバのソフトウェアフラグを設定するか、またはデバイスにコマンドを書き込むことによって、デバイス固有のモードを設定するために頻繁に使用されます。また、この制御メカニズムは、現在のデバイスの状態に関する情報をユーザーに返すためにも使用されます。つまり、この制御メカニズムは、アプリケーションやドライバで実行する必要のあるすべてのことを実行できます。

ioctl() エントリポイント (文字ドライバ)

int xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
     cred_t *credp, int *rvalp);

cmd パラメータは、ioctl(9E) がどのコマンドを実行するかを示します。慣例により、入出力制御コマンドが関連付けられているドライバは、そのコマンドのビット 8 から 15 で示されます。通常は、文字の ASCII コードがドライバを表します。ドライバ固有のコマンドはビット 0 から 7 にあります。次の例には、いくつかの入出力コマンドの作成が示されています。

#define XXIOC    ('x' << 8)     /* 'x' is a character that represents device xx */
#define XX_GET_STATUS    (XXIOC | 1)  /* get status register */
#define XX_SET_CMD       (XXIOC | 2)  /* send command */

arg の解釈は、コマンドによって異なります。入出力制御コマンドは、ドライバのドキュメントまたはマニュアルページで説明されています。コマンドはまた、アプリケーションがコマンドの名前、コマンドが何を実行するか、およびコマンドが arg として何を受け入れるか、または返すかを判定できるように、公開ヘッダーファイルでも定義されます。ドライバとの間の arg のデータ転送はすべて、ドライバによって実行される必要があります。

フレームバッファーやディスクなどの特定のクラスのデバイスは、標準的な一連の入出力制御要求をサポートする必要があります。これらの標準の入出力制御インタフェースは、Solaris 8 のリファレンスマニュアルコレクションで説明されています。たとえば、fbio(7I) ではフレームバッファーがサポートする必要のある入出力制御について、また dkio(7I) では標準のディスク入出力制御について説明しています。入出力制御の詳細については、「その他の入出力制御」を参照してください。

ドライバは、arg のデータをユーザーレベルのアプリケーションからカーネルレベルに転送するために ddi_copyin(9F) を使用する必要があります。ドライバは、カーネルレベルからユーザーレベルにデータを転送するために ddi_copyout(9F) を使用する必要があります。ddi_copyin(9F) または ddi_copyout(9F) を使用しないと、2 つの条件でパニックが発生することがあります。アーキテクチャーによってカーネルとユーザーのアドレス空間が分離されている場合、またはユーザーアドレスがスワップアウトされている場合はパニックが発生します。

ioctl(9E) は通常、サポートされている各 ioctl(9E) 要求に対するケースを含む switch 文です。

例 15-12 ioctl(9E) ルーチン

static int
xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
    cred_t *credp, int *rvalp)
{
     uint8_t        csr;
     struct xxstate     *xsp;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL) {
        return (ENXIO);
     }
     switch (cmd) {
     case XX_GET_STATUS:
       csr = ddi_get8(xsp->data_access_handle, &xsp->regp->csr);
       if (ddi_copyout(&csr, (void *)arg,
           sizeof (uint8_t), mode) != 0) {
           return (EFAULT);
       }
       break;
     case XX_SET_CMD:
       if (ddi_copyin((void *)arg, &csr,
         sizeof (uint8_t), mode) != 0) {
         return (EFAULT);
       }
       ddi_put8(xsp->data_access_handle, &xsp->regp->csr, csr);
       break;
     default:
       /* generic "ioctl unknown" error */
       return (ENOTTY);
     }
     return (0);
}

cmd 変数は、特定のデバイス制御操作を識別します。arg にユーザー仮想アドレスが含まれていると、問題が発生することがあります。ioctl(9E) は、arg によって示されたアプリケーションプログラム内のデータ構造体とドライバの間でデータを転送するために ddi_copyin(9F) または ddi_copyout(9F) を呼び出す必要があります。例 15-12 では、XX_GET_STATUS 要求のケースに対して、xsp->regp->csr の内容が arg 内のアドレスにコピーされます。ioctl(9E) は、成功した要求を行った ioctl(2) システムコールへの戻り値として任意の整数値を *rvalp 内に格納できます。負の戻り値 (-1 など) は避けるべきです。多くのアプリケーションプログラムは、負の値が失敗を示すことを前提にしています。

次の例は、前の段落で説明した入出力制御を使用するアプリケーションを示しています。

例 15-13 ioctl(9E) の使用

#include <sys/types.h>
#include "xxio.h"     /* contains device's ioctl cmds and args */
int
main(void)
{
     uint8_t    status;
     /* ... */
     /*
      * read the device status
      */
     if (ioctl(fd, XX_GET_STATUS, &status) == -1) {
         /* error handling */
     }
     printf("device status %x\n", status);
     exit(0);
}

64 ビットに対応したデバイスドライバに対する入出力制御のサポート

Solaris カーネルは、32 ビットアプリケーションと 64 ビットアプリケーションの両方をサポートしている適切なハードウェア上では 64 ビットモードで動作します。両方のサイズのプログラムからの入出力制御コマンドをサポートするには、64 ビットのデバイスドライバが必要です。32 ビットプログラムと 64 ビットプログラムの違いは、C 言語の型モデルです。32 ビットプログラムは ILP32 であり、64 ビットプログラムは LP64 です。C データ型モデルについては、付録 C 64 ビットデバイスドライバの準備を参照してください。

プログラムとカーネルの間を流れるデータの形式が同じでない場合は、ドライバがモデルの不一致に対処できる必要があります。モデルの不一致に対処するには、データに対して適切な調整を行う必要があります。

モデルの不一致が存在するかどうかを判定するために、ioctl(9E) のモードパラメータは、ドライバにデータモデルのビットを渡します。例 15-14 に示すように、このモードパラメータは次に、何らかのモデル変換が必要かどうかを判定するために ddi_model_convert_from(9F) に渡されます。

ioctl(9E) ルーチンにデータモデルを渡すために、モード引数のフラグサブフィールドが使用されます。このフラグは、次のいずれかの値に設定されます。

FNATIVE は、カーネル実装のデータモデルに一致するように条件付きで定義されます。mode 引数からフラグを抽出するには、FMODELS マスクを使用します。これにより、ドライバは、明示的にデータモデルを検査してアプリケーションのデータ構造体をコピーする方法を判定できます。

DDI 関数の ddi_model_convert_from(9F) は、ioctl() 呼び出しに関して一部のドライバを支援できる簡易ルーチンです。この関数は引数としてユーザーアプリケーションのデータ型モデルを受け取り、次のいずれかの値を返します。

アプリケーションとドライバのデータモデルが同じである場合のように、データ変換が必要ない場合は DDI_MODEL_NONE が返されます。LP64 モデルにコンパイルされていて、32 ビットアプリケーションと通信するドライバには DDI_MODEL_ILP32 が返されます。

次の例では、ドライバは、ユーザーアドレスを含むデータ構造体をコピーします。このデータ構造体は、ILP32 から LP64 にサイズが変更されます。それに応じて、64 ビットのドライバは、32 ビットアプリケーションとの通信には構造体の 32 ビットバージョンを使用します。

例 15-14 32 ビットアプリケーションと 64 ビットアプリケーションをサポートする ioctl(9E) ルーチン

struct args32 {
    uint32_t    addr;    /* 32-bit address in LP64 */
    int     len;
}
struct args {
    caddr_t     addr;    /* 64-bit address in LP64 */
    int     len;
}

static int
xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
    cred_t *credp, int *rvalp)
{
    struct  xxstate  *xsp;
    struct  args     a;
    xsp = ddi_get_soft_state(statep, getminor(dev));
    if (xsp == NULL) {
        return (ENXIO);
    }
    switch (cmd) {
    case XX_COPYIN_DATA:
        switch(ddi_model_convert_from(mode)) {
        case DDI_MODEL_ILP32:
        {
            struct args32 a32;

            /* copy 32-bit args data shape */
            if (ddi_copyin((void *)arg, &a32,
                sizeof (struct args32), mode) != 0) {
                return (EFAULT);
            }
            /* convert 32-bit to 64-bit args data shape */
            a.addr = a32.addr;
            a.len = a32.len;
            break;
        }
        case DDI_MODEL_NONE:
            /* application and driver have same data model. */
            if (ddi_copyin((void *)arg, &a, sizeof (struct args),
                mode) != 0) {
                return (EFAULT);
            }
        }
        /* continue using data shape in native driver data model. */
        break;

    case XX_COPYOUT_DATA:
        /* copyout handling */
        break;
    default:
        /* generic "ioctl unknown" error */
        return (ENOTTY);
    }
    return (0);
}

copyout() のオーバーフローの処理

ドライバでは、32 ビットのサイズの構造体には収まらないネイティブな量のコピー出力が必要になる場合があります。この場合、ドライバは、呼び出し元に EOVERFLOW を返します。次の例に示すように、EOVERFLOW は、返される値を保持するにはインタフェース内のデータ型が小さすぎることを示すものとして機能します。

例 15-15 copyout(9F) のオーバーフローの処理

int
    xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
     cred_t *cr, int *rval_p)
    {
        struct resdata res;
        /* body of driver */
        switch (ddi_model_convert_from(mode & FMODELS)) {
        case DDI_MODEL_ILP32: {
            struct resdata32 res32;

            if (res.size > UINT_MAX)
                    return (EOVERFLOW);    
            res32.size = (size32_t)res.size;
            res32.flag = res.flag;
            if (ddi_copyout(&res32,
                (void *)arg, sizeof (res32), mode))
                    return (EFAULT);
        }
        break;

        case DDI_MODEL_NONE:
            if (ddi_copyout(&res, (void *)arg, sizeof (res), mode))
                    return (EFAULT);
            break;
        }
        return (0);
    }