Oracle® Solaris 11.2 デバイスドライバの記述

印刷ビューの終了

更新: 2014 年 9 月
 
 

デバイスコンテキスト管理用のエントリポイント

デバイスコンテキストの管理に使用されるエントリポイントは、次のとおりです。

devmap_map() エントリポイント

devmap(9E) の構文は次のとおりです。

int xxdevmap_map(devmap_cookie_t handle, dev_t dev, uint_t flags,
    offset_t offset, size_t len, void **new-devprivate);

devmap_map() エントリポイントは、ドライバがその devmap() エントリポイントから返り、システムがデバイスメモリーへのユーザーマッピングを確立したあとで呼び出されます。devmap() エントリポイントを使用すると、ドライバは追加の処理を実行したり、マッピング固有の非公開データを割り当てたりできます。たとえば、コンテキストの切り替えをサポートするには、ドライバはコンテキスト構造体を割り当てる必要があります。続いてドライバは、そのコンテキスト構造体をマッピングに関連付ける必要があります。

システムは、ドライバが割り当て済みの非公開データへのポインタを *new-devprivate に返すことを予期します。ドライバは、マッピングの範囲を定義する offsetlen をその非公開データ内に格納する必要があります。あとでシステムが devmap_unmap(9E) を呼び出したときに、ドライバはこの情報に基づいて、マッピングの対応づけを解除する量がどれだけになるのかを判断します。

flags は、ドライバがマッピングの非公開コンテキストを割り当てるかどうかを示します。たとえば、flagsMAP_PRIVATE に設定されている場合、ドライバはデバイスコンテキストを格納するためのメモリー領域を割り当てることができます。MAP_SHARED が設定されている場合、ドライバは共有領域へのポインタを返します。

次の例は、devmap() エントリポイントを示したものです。ドライバは新しいコンテキスト構造体を割り当てています。続いてドライバは、エントリポイントから渡された関連パラメータを保存しています。次に、マッピングに新しいコンテキストを割り当てるために、コンテキストの割り当てを行うか、すでに存在している共有コンテキストにマッピングを関連付けています。マッピングがデバイスにアクセスする最小時間間隔は、1 ミリ秒に設定されています。

使用例 11-1  devmap() ルーチンの使用
static int
int xxdevmap_map(devmap_cookie_t handle, dev_t dev, uint_t flags,
    offset_t offset, size_t len, void **new_devprivate)
{
    struct xxstate *xsp = ddi_get_soft_state(statep,
                  getminor(dev));
    struct xxctx *newctx;

    /* create a new context structure */
    newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP);
    newctx->xsp = xsp;
    newctx->handle = handle;
    newctx->offset = offset;
    newctx->flags = flags;
    newctx->len = len;
    mutex_enter(&xsp->ctx_lock);
    if (flags & MAP_PRIVATE) {
        /* allocate a private context and initialize it */
        newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP);
        xxctxinit(newctx);
    } else {
        /* set a pointer to the shared context */
        newctx->context = xsp->ctx_shared;
    }
    mutex_exit(&xsp->ctx_lock);
    /* give at least 1 ms access before context switching */
    devmap_set_ctx_timeout(handle, drv_usectohz(1000));
    /* return the context structure */
    *new_devprivate = newctx;
    return(0);
}

devmap_access() エントリポイント

devmap_access(9E) エントリポイントは、変換が無効になっているマッピングへのアクセスが発生した場合に呼び出されます。マッピングの変換が無効化されるのは、mmap(2) への応答として devmap_devmem_setup(9F) でマッピングが作成された場合、fork(2) によってマッピングが複製された場合、または devmap_unload(9F) 呼び出しによってマッピングが明示的に無効化された場合です。

devmap_access() の構文は次のとおりです。

int xxdevmap_access(devmap_cookie_t handle, void *devprivate,
    offset_t offset, size_t len, uint_t type, uint_t rw);

ここでは:

handle

ユーザープロセスからアクセスされたマッピングのマッピングハンドル。

devprivate

マッピングに関連付けられたドライバ非公開データへのポインタ。

offset

アクセスされたマッピング内のオフセット。

len

アクセス対象となるメモリーのバイト単位の長さ。

type

アクセス操作のタイプ。

rw

アクセスの向きを指定します。

システムは、devmap_access(9E)devmap_do_ctxmgt (9F)devmap_default_access(9F) のいずれかを呼び出してメモリーアドレス変換をロードしたあとで、devmap_access() が復帰することを想定します。コンテキスト切り替えをサポートするマッピングの場合、デバイスドライバは devmap_do_ctxmgt() を呼び出します。このルーチンには、devmap_access(9E) からのすべてのパラメータと、コンテキスト切り替えを処理するドライバエントリポイント devmap_contextmgt (9E) へのポインタが渡されます。コンテキスト切り替えをサポートしないマッピングの場合、ドライバは devmap_default_access(9F) を呼び出します。devmap_default_access() の目的は、devmap_load(9F) を呼び出してユーザー変換をロードすることです。

次の例は、devmap_access(9E) エントリポイントを示したものです。マッピングは 2 つの領域に分割されています。オフセット OFF_CTXMG から CTXMGT_SIZE バイトの長さの領域は、コンテキスト管理をサポートします。 マッピングの残りの部分はデフォルトアクセスをサポートします。

使用例 11-2  devmap_access() ルーチンの使用
#define OFF_CTXMG      0
#define CTXMGT_SIZE    0x20000    
static int
xxdevmap_access(devmap_cookie_t handle, void *devprivate,
    offset_t off, size_t len, uint_t type, uint_t rw)
{
    offset_t diff;
    int    error;

    if ((diff = off - OFF_CTXMG) >= 0 && diff < CTXMGT_SIZE) {
        error = devmap_do_ctxmgt(handle, devprivate, off,
            len, type, rw, xxdevmap_contextmgt);
    } else {
        error = devmap_default_access(handle, devprivate,
            off, len, type, rw);
    }
    return (error);
}

devmap_contextmgt() エントリポイント

devmap_contextmgt(9E) の構文は次のとおりです。

int xxdevmap_contextmgt(devmap_cookie_t handle, void *devprivate,
    offset_t offset, size_t len, uint_t type, uint_t rw);

devmap_contextmgt() は、デバイスに現在アクセスしているマッピングのハンドルを指定して devmap_unload(9F) を呼び出します。このアプローチにより、そのマッピングの変換が無効化されます。このアプローチにより、現在のマッピングへのアクセスが次回発生したときに、そのマッピングで devmap_access(9E) の呼び出しが発生するようになります。アクセスイベントが発生する原因となったマッピングのマッピング変換を有効化する必要があります。したがって、ドライバは、アクセスを要求しているプロセスのデバイスコンテキストを復元する必要があります。さらにドライバは、このエントリポイントへの呼び出しを生成したマッピングの handle を指定して devmap_load(9F) を呼び出す必要もあります。

devmap_load() 呼び出しによってマッピング変換が有効化されたマッピング部分へのアクセスが発生しても、devmap_access() への呼び出しは生成されません。その後、devmap_unload() を呼び出すと、マッピング変換が無効化されます。この呼び出しによって、devmap_access () がふたたび呼び出されるようになります。

devmap_load() または devmap_unl ad() のいずれかからエラーが返された場合、devmap_contextmgt() はただちにそのエラーを返します。デバイスドライバがデバイスコンテキストの復元中にハードウェア障害を検出した場合、-1 が返されます。それ以外の場合、アクセス要求の処理が成功したら、devmap_contextmgt() はゼロを返します。devmap_contextmgt() からゼロ以外の値が返された場合、SIGBUS または SIGSEGV がプロセスに送信されます。

次の例は、ページ数 1 のデバイスコンテキストを管理する方法を示したものです。


注 - xxctxsave()xxctxrestore() は、コンテキストを保存および復元するためのデバイス依存関数です。xxctxsave() は、レジスタからデータを読み取り、そのデータをソフト状態構造体に保存します。 xxctxrestore() は、ソフト状態構造体に保存されたデータを取得し、そのデータをデバイスのレジスタに書き込みます。読み取り、書き込み、保存はすべて、DDI/DKI データアクセスルーチンを使用して実行します。
使用例 11-3  devmap_contextmgt() ルーチンの使用
static int
xxdevmap_contextmgt(devmap_cookie_t handle, void *devprivate,
    offset_t off, size_t len, uint_t type, uint_t rw)
{
    int    error;
    struct xxctx *ctxp = devprivate;
    struct xxstate *xsp = ctxp->xsp;
    mutex_enter(&xsp->ctx_lock);
    /* unload mapping for current context */
    if (xsp->current_ctx != NULL) {
        if ((error = devmap_unload(xsp->current_ctx->handle,
            off, len)) != 0) {
            xsp->current_ctx = NULL;
            mutex_exit(&xsp->ctx_lock);
            return (error);
        }
    }
    /* Switch device context - device dependent */
    if (xxctxsave(xsp->current_ctx, off, len) < 0) {
        xsp->current_ctx = NULL;
        mutex_exit(&xsp->ctx_lock);
        return (-1);
    }
    if (xxctxrestore(ctxp, off, len) < 0){
        xsp->current_ctx = NULL;
        mutex_exit(&xsp->ctx_lock);
        return (-1);
    }
    xsp->current_ctx = ctxp;
    /* establish mapping for new context and return */
    error = devmap_load(handle, off, len, type, rw);
    if (error)
        xsp->current_ctx = NULL;
    mutex_exit(&xsp->ctx_lock);
    return (error);
}

devmap_dup() エントリポイント

devmap_dup(9E) エントリポイントは、fork(2) を呼び出すユーザープロセスなどによってデバイスマッピングが複製されるときに呼び出されます。ドライバからは、新しいマッピングのための新しいドライバ非公開データが生成されることが予期されます。

devmap_dup() の構文は次のとおりです。

int xxdevmap_dup(devmap_cookie_t handle, void *devprivate,
    devmap_cookie_t new-handle, void **new-devprivate);

ここでは:

handle

複製されるマッピングのマッピングハンドル。

new-handle

複製されたマッピングのマッピングハンドル。

devprivate

複製されるマッピングに関連付けられたドライバ非公開データへのポインタ。

*new-devprivate

新しいマッピングの新しいドライバ非公開データを指すように設定します。

devmap_dup() で作成されたマッピングではデフォルトで、マッピング変換が無効化されています。マッピング変換が無効になっていると、マッピングへのアクセスがはじめて発生したときに devmap_access(9E) エントリポイントが強制的に呼び出されます。

次の例は、典型的な devmap_dup() ルーチンを示したものです。

使用例 11-4  devmap_dup() ルーチンの使用
static int
xxdevmap_dup(devmap_cookie_t handle, void *devprivate,
    devmap_cookie_t new_handle, void **new_devprivate)
{
    struct xxctx *ctxp = devprivate;
    struct xxstate *xsp = ctxp->xsp;
    struct xxctx *newctx;
    /* Create a new context for the duplicated mapping */
    newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP);
    newctx->xsp = xsp;
    newctx->handle = new_handle;
    newctx->offset = ctxp->offset;
    newctx->flags = ctxp->flags;
    newctx->len = ctxp->len;
    mutex_enter(&xsp->ctx_lock);
    if (ctxp->flags & MAP_PRIVATE) {
        newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP);
        bcopy(ctxp->context, newctx->context, XXCTX_SIZE);
    } else {
        newctx->context = xsp->ctx_shared;
    }
    mutex_exit(&xsp->ctx_lock);
    *new_devprivate = newctx;
    return(0);
}

devmap_unmap() エントリポイント

devmap_unmap(9E) エントリポイントは、マッピングの対応づけを解除するときに呼び出されます。対応づけの解除が発生する可能性があるのは、ユーザープロセスが終了するときと、munmap(2) システムコールが呼び出されたときです。

devmap_unmap() の構文は次のとおりです。

void xxdevmap_unmap(devmap_cookie_t handle, void *devprivate,
    offset_t off, size_t len, devmap_cookie_t new-handle1,
    void **new-devprivate1, devmap_cookie_t new-handle2,
    void **new-devprivate2);

ここでは:

handle

解放されるマッピングのマッピングハンドル。

devprivate

マッピングに関連付けられたドライバ非公開データへのポインタ。

off

対応づけ解除の開始位置となる、論理デバイスメモリー内のオフセット。

len

対応づけ解除の対象となるメモリーのバイト単位の長さ。

new-handle1

off - 1 で終わる新しい領域を記述するためにシステムが使用するハンドル。new-handle1 の値は NULL でもかまいません。

new-devprivate1

ドライバによる、off -1 で終わる新しい領域の非公開ドライバマッピングデータの設定先となるポインタ。new-handle1NULL の場合、new-devprivate1 は無視されます。

new-handle2

off + len から始まる新しい領域を記述するためにシステムが使用するハンドル。new-handle2 の値は NULL でもかまいません。

new-devprivate2

ドライバによる、off + len から始まる新しい領域のドライバ非公開マッピングデータの設定先となるポインタ。new-handle2NULL の場合、new-devprivate2 は無視されます。

マッピングの作成時には、devmap_unmap() ルーチンが devmap_map(9E) または devmap_dup(9E) のいずれかによって割り当てられたすべてのドライバ非公開リソースを解放することが予期されます。対応づけ解除の対象がマッピングの一部のみである場合、ドライバは、残りのマッピングに対する新しい非公開データを割り当てたあとで、古い非公開データを解放する必要があります。解放されるマッピングのハンドルが、有効な変換を含むマッピングを指している場合でも、そのハンドルを指定して devmap_unload (9F) を呼び出す必要はありません。ただし、あとで devmap_access(9E) で問題が発生しないように、現在のマッピング表現は「現在のマッピングがない」状態に設定されていることを、デバイスドライバ内で確認するようにしてください。

次の例は、典型的な devmap_unmap() ルーチンを示したものです。

使用例 11-5  devmap_unmap() ルーチンの使用
static void
xxdevmap_unmap(devmap_cookie_t handle, void *devprivate,
    offset_t off, size_t len, devmap_cookie_t new_handle1,
    void **new_devprivate1, devmap_cookie_t new_handle2,
    void **new_devprivate2)
{
    struct xxctx *ctxp = devprivate;
    struct xxstate *xsp = ctxp->xsp;
    mutex_enter(&xsp->ctx_lock);
    /*
     * If new_handle1 is not NULL, we are unmapping
     * at the end of the mapping.
     */
    if (new_handle1 != NULL) {
        /* Create a new context structure for the mapping */
        newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP);
        newctx->xsp = xsp;
        if (ctxp->flags & MAP_PRIVATE) {
            /* allocate memory for the private context and copy it */
            newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP);
            bcopy(ctxp->context, newctx->context, XXCTX_SIZE);
        } else {
            /* point to the shared context */
            newctx->context = xsp->ctx_shared;
        }
        newctx->handle = new_handle1;
        newctx->offset = ctxp->offset;
        newctx->len = off - ctxp->offset;
        *new_devprivate1 = newctx;
    }
    /*
     * If new_handle2 is not NULL, we are unmapping
     * at the beginning of the mapping.
     */
    if (new_handle2 != NULL) {
        /* Create a new context for the mapping */
        newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP);
        newctx->xsp = xsp;
        if (ctxp->flags & MAP_PRIVATE) {
            newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP);
            bcopy(ctxp->context, newctx->context, XXCTX_SIZE);
        } else {
            newctx->context = xsp->ctx_shared;
        }
        newctx->handle = new_handle2;
        newctx->offset = off + len;
        newctx->flags = ctxp->flags;
        newctx->len = ctxp->len - (off + len - ctxp->off);
        *new_devprivate2 = newctx;
    }
    if (xsp->current_ctx == ctxp)
        xsp->current_ctx = NULL;
    mutex_exit(&xsp->ctx_lock);
    if (ctxp->flags & MAP_PRIVATE)
        kmem_free(ctxp->context, XXCTX_SIZE);
    kmem_free(ctxp, sizeof (struct xxctx));
}