このセクションでは、この章の前のセクションで説明した LDI 呼び出しを使用するカーネルデバイスコンシューマの例を示します。このセクションでは、このモジュール例の次の面について説明します。
このカーネルデバイスコンシューマの例は lyr という名前です。lyr モジュールは、LDI 呼び出しを使用してターゲットデバイスにデータを送信する階層化ドライバです。lyr ドライバは open(9E) エントリポイントで、lyr.conf 構成ファイル内の lyr_targ プロパティーによって指定されたデバイスを開きます。lyr ドライバは write(9E) エントリポイントで、lyr_targ プロパティーによって指定されたデバイスに、すべての受信データを書き込みます。
次に示す構成ファイルでは、lyr ドライバの書き込み先ターゲットデバイスはコンソールです。
使用例 57 構成ファイル# # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" name="lyr" parent="pseudo" instance=1; lyr_targ="/dev/console";
次に示したドライバソースファイルで、lyr_state_t 構造体は lyr ドライバのソフト状態を保持しています。ソフト状態には、lyr_targ デバイスの階層化ドライバのハンドル (lh) と、lyr デバイスの階層化識別子 ( li) が格納されています。ソフト状態の詳細については、ドライバのソフト状態情報の取得を参照してください。
lyr_open() エントリポイントで、ddi_prop_lookup_string(9F) は lyr_targ プロパティーから、lyr デバイスが開くターゲットデバイスの名前を取得しています。ldi_ident_from_dev(9F) 関数は lyr デバイスの LDI 階層化識別子を取得します。ldi_open_by_name(9F) 関数は lyr_targ デバイスを開き、lyr_targ デバイスの階層化ドライバのハンドルを取得します。
lyr_open() で何らかのエラーが発生した場合、ldi_close(9F)、ldi_ident_release(9F)、および ddi_prop_free(9F) の呼び出しによって、実行されたすべてのことが取り消されます。ldi_close(9F) 関数は lyr_targ デバイスを閉じます。ldi_ident_release(9F) 関数は lyr 階層化識別子を解放します。ddi_prop_free(9F) 関数は、lyr_targ デバイス名が取得されたときに割り当てられたリソースを解放します。エラーが発生しなければ、lyr_close() エントリポイントで ldi_close(9F) 関数と ldi_ident_release(9F) 関数が呼び出されます。
ドライバモジュールの最終行では、ldi_write(9F) 関数が呼び出されています。ldi_write(9F) 関数は lyr_write() エントリポイントで lyr デバイスに書き込まれたデータを取得し、そのデータを lyr_targ デバイスに書き込みます。 ldi_write(9F) 関数は lyr_targ デバイスの階層化ドライバのハンドルを使用して、データを lyr_targ デバイスに書き込みます。
使用例 58 ドライバソースファイル#include <sys/types.h> #include <sys/file.h> #include <sys/errno.h> #include <sys/open.h> #include <sys/cred.h> #include <sys/cmn_err.h> #include <sys/modctl.h> #include <sys/conf.h> #include <sys/stat.h> #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sunldi.h> typedef struct lyr_state { ldi_handle_t lh; ldi_ident_t li; dev_info_t *dip; minor_t minor; int flags; kmutex_t lock; } lyr_state_t; #define LYR_OPENED 0x1 /* lh is valid */ #define LYR_IDENTED 0x2 /* li is valid */ static int lyr_info(dev_info_t *, ddi_info_cmd_t, void *, void **); static int lyr_attach(dev_info_t *, ddi_attach_cmd_t); static int lyr_detach(dev_info_t *, ddi_detach_cmd_t); static int lyr_open(dev_t *, int, int, cred_t *); static int lyr_close(dev_t, int, int, cred_t *); static int lyr_write(dev_t, struct uio *, cred_t *); static void *lyr_statep; static struct cb_ops lyr_cb_ops = { lyr_open, /* open */ lyr_close, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ lyr_write, /* write */ nodev, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* prop_op */ NULL, /* streamtab */ D_NEW | D_MP, /* cb_flag */ CB_REV, /* cb_rev */ nodev, /* aread */ nodev /* awrite */ }; static struct dev_ops lyr_dev_ops = { DEVO_REV, /* devo_rev, */ 0, /* refcnt */ lyr_info, /* getinfo */ nulldev, /* identify */ nulldev, /* probe */ lyr_attach, /* attach */ lyr_detach, /* detach */ nodev, /* reset */ &lyr_cb_ops, /* cb_ops */ NULL, /* bus_ops */ NULL, /* power */ ddi_quiesce_not_needed, /* quiesce */ }; static struct modldrv modldrv = { &mod_driverops, "LDI example driver", &lyr_dev_ops }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; int _init(void) { int rv; if ((rv = ddi_soft_state_init(&lyr_statep, sizeof (lyr_state_t), 0)) != 0) { cmn_err(CE_WARN, "lyr _init: soft state init failed\n"); return (rv); } if ((rv = mod_install(&modlinkage)) != 0) { cmn_err(CE_WARN, "lyr _init: mod_install failed\n"); goto FAIL; } return (rv); /*NOTEREACHED*/ FAIL: ddi_soft_state_fini(&lyr_statep); return (rv); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } int _fini(void) { int rv; if ((rv = mod_remove(&modlinkage)) != 0) { return(rv); } ddi_soft_state_fini(&lyr_statep); return (rv); } /* * 1:1 mapping between minor number and instance */ static int lyr_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { int inst; minor_t minor; lyr_state_t *statep; char *myname = "lyr_info"; minor = getminor((dev_t)arg); inst = minor; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: statep = ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: get soft state " "failed on inst %d\n", myname, inst); return (DDI_FAILURE); } *result = (void *)statep->dip; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)inst; break; default: break; } return (DDI_SUCCESS); } static int lyr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int inst; lyr_state_t *statep; char *myname = "lyr_attach"; switch (cmd) { case DDI_ATTACH: inst = ddi_get_instance(dip); if (ddi_soft_state_zalloc(lyr_statep, inst) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ddi_soft_state_zallac failed " "on inst %d\n", myname, inst); goto FAIL; } statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); goto FAIL; } statep->dip = dip; statep->minor = inst; if (ddi_create_minor_node(dip, "node", S_IFCHR, statep->minor, DDI_PSEUDO, 0) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s: ddi_create_minor_node failed on " "inst %d\n", myname, inst); goto FAIL; } mutex_init(&statep->lock, NULL, MUTEX_DRIVER, NULL); return (DDI_SUCCESS); case DDI_RESUME: case DDI_PM_RESUME: default: break; } return (DDI_FAILURE); /*NOTREACHED*/ FAIL: ddi_soft_state_free(lyr_statep, inst); ddi_remove_minor_node(dip, NULL); return (DDI_FAILURE); } static int lyr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int inst; lyr_state_t *statep; char *myname = "lyr_detach"; inst = ddi_get_instance(dip); statep = ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: get soft state failed on " "inst %d\n", myname, inst); return (DDI_FAILURE); } if (statep->dip != dip) { cmn_err(CE_WARN, "%s: soft state does not match devinfo " "on inst %d\n", myname, inst); return (DDI_FAILURE); } switch (cmd) { case DDI_DETACH: mutex_destroy(&statep->lock); ddi_soft_state_free(lyr_statep, inst); ddi_remove_minor_node(dip, NULL); return (DDI_SUCCESS); case DDI_SUSPEND: case DDI_PM_SUSPEND: default: break; } return (DDI_FAILURE); } /* * on this driver's open, we open the target specified by a property and store * the layered handle and ident in our soft state. a good target would be * "/dev/console" or more interestingly, a pseudo terminal as specified by the * tty command */ /*ARGSUSED*/ static int lyr_open(dev_t *devtp, int oflag, int otyp, cred_t *credp) { int rv, inst = getminor(*devtp); lyr_state_t *statep; char *myname = "lyr_open"; dev_info_t *dip; char *lyr_targ = NULL; statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); return (EIO); } dip = statep->dip; /* * our target device to open should be specified by the "lyr_targ" * string property, which should be set in this driver's .conf file */ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_NOTPROM, "lyr_targ", &lyr_targ) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s: ddi_prop_lookup_string failed on " "inst %d\n", myname, inst); return (EIO); } /* * since we only have one pair of lh's and li's available, we don't * allow multiple on the same instance */ mutex_enter(&statep->lock); if (statep->flags & (LYR_OPENED | LYR_IDENTED)) { cmn_err(CE_WARN, "%s: multiple layered opens or idents " "from inst %d not allowed\n", myname, inst); mutex_exit(&statep->lock); ddi_prop_free(lyr_targ); return (EIO); } rv = ldi_ident_from_dev(*devtp, &statep->li); if (rv != 0) { cmn_err(CE_WARN, "%s: ldi_ident_from_dev failed on inst %d\n", myname, inst); goto FAIL; } statep->flags |= LYR_IDENTED; rv = ldi_open_by_name(lyr_targ, FREAD | FWRITE, credp, &statep->lh, statep->li); if (rv != 0) { cmn_err(CE_WARN, "%s: ldi_open_by_name failed on inst %d\n", myname, inst); goto FAIL; } statep->flags |= LYR_OPENED; cmn_err(CE_CONT, "\n%s: opened target '%s' successfully on inst %d\n", myname, lyr_targ, inst); rv = 0; FAIL: /* cleanup on error */ if (rv != 0) { if (statep->flags & LYR_OPENED) (void)ldi_close(statep->lh, FREAD | FWRITE, credp); if (statep->flags & LYR_IDENTED) ldi_ident_release(statep->li); statep->flags &= ~(LYR_OPENED | LYR_IDENTED); } mutex_exit(&statep->lock); if (lyr_targ != NULL) ddi_prop_free(lyr_targ); return (rv); } /* * on this driver's close, we close the target indicated by the lh member * in our soft state and release the ident, li as well. in fact, we MUST do * both of these at all times even if close yields an error because the * device framework effectively closes the device, releasing all data * associated with it and simply returning whatever value the target's * close(9E) returned. therefore, we must as well. */ /*ARGSUSED*/ static int lyr_close(dev_t devt, int oflag, int otyp, cred_t *credp) { int rv, inst = getminor(devt); lyr_state_t *statep; char *myname = "lyr_close"; statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); return (EIO); } mutex_enter(&statep->lock); rv = ldi_close(statep->lh, FREAD | FWRITE, credp); if (rv != 0) { cmn_err(CE_WARN, "%s: ldi_close failed on inst %d, but will ", "continue to release ident\n", myname, inst); } ldi_ident_release(statep->li); if (rv == 0) { cmn_err(CE_CONT, "\n%s: closed target successfully on " "inst %d\n", myname, inst); } statep->flags &= ~(LYR_OPENED | LYR_IDENTED); mutex_exit(&statep->lock); return (rv); } /* * echo the data we receive to the target */ /*ARGSUSED*/ static int lyr_write(dev_t devt, struct uio *uiop, cred_t *credp) { int rv, inst = getminor(devt); lyr_state_t *statep; char *myname = "lyr_write"; statep = (lyr_state_t *)ddi_get_soft_state(lyr_statep, inst); if (statep == NULL) { cmn_err(CE_WARN, "%s: ddi_get_soft_state failed on " "inst %d\n", myname, inst); return (EIO); } return (ldi_write(statep->lh, uiop, credp)); }
–D_KERNEL オプションを使用して、これがカーネルモジュールであることを示します。
SPARC アーキテクチャー向けにコンパイルする場合は、–xarch=v9 オプションを使用します。
% cc -c -D_KERNEL -xarch=v9 lyr.c
32 ビット x86 アーキテクチャー向けにコンパイルする場合は、次のコマンドを使用します。
% cc -c -D_KERNEL lyr.c
% ld -r -o lyr lyr.o
root ユーザーとして、システムのカーネルドライバ領域に構成ファイルをコピーします。
# cp lyr.conf /usr/kernel/drv
root ユーザーとして、SPARC アーキテクチャー上の sparcv9 ドライバ領域にドライババイナリをコピーします。
# cp lyr /usr/kernel/drv/sparcv9
root ユーザーとして、32 ビット x86 アーキテクチャー上の drv ドライバ領域にドライババイナリをコピーします。
# cp lyr /usr/kernel/drv
root ユーザーとして、add_drv(1M) コマンドを使用してドライバをロードします。
# add_drv lyr
疑似デバイスを一覧表示して、lyr デバイスが存在するようになったことを確認します。
# ls /devices/pseudo | grep lyr lyr@1 lyr@1:node
lyr ドライバをテストするには、lyr デバイスにメッセージを書き込み、メッセージが lyr_targ デバイスに表示されることを確認します。
使用例 59 階層化デバイスへの短いメッセージの書き込みこの例では、lyr_targ デバイスが、lyr デバイスがインストールされているシステムのコンソールです。
参照している表示が、lyr デバイスがインストールされているシステムのコンソールデバイスの表示でもある場合は、コンソールに書き込むと表示が壊れることに注意してください。コンソールメッセージはウィンドウシステムの外側に表示されます。lyr ドライバのテスト後、表示を再描画またはリフレッシュする必要があります。
参照している表示が、lyr デバイスがインストールされているシステムのコンソールデバイスの表示ではない場合、ログインするなどして、ターゲットコンソールデバイスの表示内容を取得します。
次のコマンドでは、非常に短いメッセージが lyr デバイスに書き込まれます。
# echo "\n\n\t===> Hello World!! <===\n" > /devices/pseudo/lyr@1:node
ターゲットコンソールに次のメッセージが表示されます。
console login: ===> Hello World!! <=== lyr: lyr_open: opened target '/dev/console' successfully on inst 1 lyr: lyr_close: closed target successfully on inst 1
lyr_open() および lyr_close() からのメッセージは、lyr_open() および lyr_close() のエントリポイントの cmn_err(9F) 呼び出しからのものです。
使用例 60 階層化デバイスへの長いメッセージの書き込み次のコマンドでは、長いメッセージが lyr デバイスに書き込まれます。
# cat lyr.conf > /devices/pseudo/lyr@1:node
ターゲットコンソールに次のメッセージが表示されます。
lyr: lyr_open: opened target '/dev/console' successfully on inst 1 # # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" name="lyr" parent="pseudo" instance=1; lyr_targ="/dev/console"; lyr: lyr_close: closed target successfully on inst 1使用例 61 ターゲットデバイスの変更
ターゲットデバイスを変更するには、/usr/kernel/drv/lyr.conf を編集し、lyr_targ プロパティーの値を、異なるターゲットデバイスへのパスに変更します。たとえば、ローカル端末での tty コマンドの出力をターゲットデバイスにできます。/dev/pts/4 はそうしたデバイスパスの例です。
新しいターゲットデバイスを使用するようにドライバを更新する前に、lyr デバイスを使用中でないことを確認してください。
# modinfo -c | grep lyr 174 3 lyr UNLOADED/UNINSTALLED
lyr.conf 構成ファイルを再ロードするには、update_drv(1M) コマンドを使用します。
# update_drv lyr
もう一度 lyr デバイスにメッセージを書き込み、メッセージが新しい lyr_targ デバイスに表示されることを確認します。