デバイス管理機能は、μT-Kernel上で動作するデバイスドライバを管理するための機能である。
デバイスドライバとは、主としてハードウェアのデバイスの操作や入出力を行うために、μT-Kernel本体とは独立して実装されるプログラムである。アプリケーションやミドルウェアによるデバイスの操作や入出力を、デバイスドライバ経由で行うことにより、個々のデバイスの仕様の差異をデバイスドライバ側で吸収し、アプリケーションやミドルウェアのハードウェア非依存性や互換性を高めることができる。
デバイス管理機能には、デバイスドライバを定義するための機能、すなわちμT-Kernelにデバイスドライバを登録するための機能と、登録済みのデバイスドライバをアプリケーションやミドルウェアから利用するための機能が含まれる。
デバイスドライバの登録は、システム起動時の初期化処理の中で行う場合が多いが、システムの通常の動作中に動的に行うことも可能である。デバイスドライバの登録は tk_def_dev によって行われ、このAPIのパラメータの一つであるデバイス登録情報(ddev
)の中で、デバイスドライバの実際の処理を行うプログラムの関数(ドライバ処理関数)群を指定する。この中には、デバイスのオープン時に呼び出されるオープン関数(openfn)、読込み時や書込みの処理開始時に呼び出される処理開始関数(execfn)、読込み時や書込みの処理の完了を待つ完了待ち関数(waitfn)などがあり、これらのドライバ処理関数の中で、実際のデバイスの操作やデバイスとの入出力の処理を行う。
これらのドライバ処理関数は、準タスク部として保護レベル0で実行されるため、ハードウェアに直接アクセスすることも可能である。デバイスとの入出力などの処理は、これらのドライバ処理関数の中で直接行う場合もあるし、これらのドライバ処理関数の中から出された要求に応じて動作する別タスク等の中で行う場合もある。これらのドライバ処理関数が呼び出される際のパラメータ等の仕様は、デバイスドライバインタフェースとして規定される。デバイスドライバインタフェースは、デバイスドライバとμT-Kernelのデバイス管理機能との間のインタフェースである。
デバイスドライバのプログラムは、その保守性や移植性を高めるために、インタフェース層、論理層、物理層の3段階の階層分けを意識して実装することが推奨される。インタフェース層はμT-Kernelのデバイス管理機能とデバイスドライバとの間のインタフェースを処理する部分、論理層はデバイスの種類に応じて共通の処理を行う部分、物理層は実際のハードウェアや制御チップに依存した処理を行う部分である。ただし、インタフェース層、論理層、物理層の間のインタフェースについて、μT-Kernelで仕様を規定しているわけではなく、実際の階層分けについては、個々のデバイスドライバにおいて最適な実装を行うことができる。インタフェース層については、個々のデバイスに依存しない共通の処理が多いため、インタフェース層の処理を行うプログラムがライブラリとして提供される場合がある。
一方、登録済みのデバイスドライバをアプリケーションやミドルウェアから利用するために、オープン(tk_opn_dev)、クローズ(tk_cls_dev)、読込み(tk_rea_dev)、書込み(tk_wri_dev)などがAPIとして提供されている。これらのAPIの仕様をアプリケーションインタフェースと呼ぶ。たとえば、アプリケーションがデバイスをオープンするために tk_opn_dev を実行した場合、μT-Kernelは、対応するデバイスドライバのオープン関数(openfn)を呼び出して、デバイスのオープンの処理を依頼する。
μT-Kernelのデバイス管理機能の位置付けと構成を図1に示す。
補足事項 | |
---|---|
デバイスドライバは、μT-Kernel本体とは独立して実装され、μT-Kernelの機能に対して拡張や追加を行うシステムプログラムであるという点で、サブシステムと共通の特徴を持つ。また、保護レベル0で動作し、ハードウェアへのアクセスが可能である点についても、両者とも共通である。一方、両者の相違点としては、デバイスドライバを呼び出す際のAPIがオープン/クローズ、リード/ライト型に固定されているのに対して、サブシステムを呼び出す際のAPIは自由に定義できる。 デバイス管理機能によって管理されるμT-Kernelのデバイスドライバは、物理的な意味でのデバイスやハードウェアに対するドライバを想定した機能であるが、物理的な意味でのデバイスやハードウェアを扱うことが必須ではない。一方、デバイスを操作するためのシステムプログラムであっても、オープン/クローズ、リード/ライト型のAPIが馴染まない場合など、デバイスドライバではなくサブシステムとして実装されることがある。 |
デバイスには、物理的なハードウェアとしてのデバイスを表す物理デバイスのほかに、ソフトウェアから見たデバイスの単位である論理デバイスがある。
多くのデバイスにおいて両者は一致するが、ハードディスクやストレージ系のデバイス(SDカード, USBストレージなど)の中に区画(パーティション)を作った場合には、デバイス全体が物理デバイス、1つの区画が論理デバイスとなる。
同種類の物理デバイスは「ユニット」により区別され、1つの物理デバイス内の論理デバイスは「サブユニット」により区別される。たとえば、1台目のハードディスクと2台目のハードディスクを区別する情報が「ユニット」であり、1台目のハードディスク内の1つ目の区画と2つ目の区画を区別する情報が「サブユニット」である。
デバイス管理機能で使用するデータの定義は、以下の通りである。なお、以下の説明のうち、具体的なデバイス名やデバイスに依存した属性などに言及した部分は、μT-Kernelの仕様ではなく、デバイスドライバの仕様に対する共通的なガイドラインを定めたものである。各デバイスドライバは、必ずしもここに定められたすべての機能を実装する必要はないが、各デバイスドライバの説明は、以下の仕様と矛盾のないように定める必要がある。
デバイス名は、デバイス毎につけられた最大8文字の文字列であり、文字コードはUS-ASCIIを使用する。デバイス名は次の要素により構成される。
#define L_DEVNM 8 /* デバイス名の長さ */
デバイスの種別を示す名前
使用可能な文字は a~z A~Z
物理的なデバイスを示す英字1文字
a~zでユニットごとにaから順に割り当てる
論理的なデバイスを示す数字最大3文字
0~254でサブユニットごとに0から順に割り当てる
デバイス名は、種別+ユニット+サブユニットの形式で表すが、ユニット、サブユニットはデバイスによっては存在しない場合もあり、その場合はそれぞれのフィールドは存在しない。
サブユニットは、ハードディスクなどの区画(パーティション)を区別するために使用するが、それ以外のデバイスにおいても、1つの物理デバイスの中に複数の論理的なデバイスを設けたい場合に使用できる。
種別+ユニットの形式を物理デバイス名と呼ぶ。また、種別+ユニット+サブユニットを論理デバイス名と呼ぶ。サブユニットがない場合は、物理デバイス名と論理デバイス名は同じになる。単にデバイス名と言ったときは、論理デバイス名を指す。
デバイス(デバイスドライバ)を μT-Kernel/SMに登録することにより、デバイス(物理デバイス名)に対してデバイスID(>0)が割り当てられる。デバイスIDは物理デバイスごとに割り当てられ、論理デバイスのデバイスIDは物理デバイスに割り当てられたデバイスIDにサブユニット番号+1(1~255)を加えたものとなる。
devid: 登録時に割り当てられたデバイスID devid 物理デバイス devid + n+1 n 番目のサブユニット(論理デバイス)
各デバイスの特徴を表すとともに、デバイスの種類分けを行うため、デバイス属性を定義する。デバイス属性は、デバイスドライバを登録する際に指定する。
デバイス属性の指定方法は、次の通りである。
IIII IIII IIII IIII PRxx xxxx KKKK KKKK
上位16ビットは、デバイス依存属性で、デバイスごとに定義される。下位16ビットは、標準属性で、下記のように定義される。
#define TD_PROTECT 0x8000 /* P: 書込み禁止 */ #define TD_REMOVABLE 0x4000 /* R: メディアの取り外し可能 */ #define TD_DEVKIND 0x00ff /* K: デバイス/メディア種別 */ #define TD_DEVTYPE 0x00f0 /* デバイスタイプ */ /* デバイスタイプ */ #define TDK_UNDEF 0x0000 /* 未定義/不明 */ #define TDK_DISK 0x0010 /* ディスクデバイス */
μT-Kernelの範囲では、ディスクタイプ以外のデバイスタイプは定義されておらず、ディスクタイプ以外のデバイスタイプを定義しても、μT-Kernelの動作には影響しない。未定義のデバイスは、未定義 TDK_UNDEF とする。
ディスクデバイスの場合には、さらに、ディスク種別が定義される。代表的なディスク種別は以下の通りである。
/* ディスク種別 */ #define TDK_DISK_UNDEF 0x0010 /* その他のディスク */ #define TDK_DISK_RAM 0x0011 /* RAMディスク(主メモリ使用) */ #define TDK_DISK_ROM 0x0012 /* ROMディスク(主メモリ使用) */ #define TDK_DISK_FLA 0x0013 /* Flash ROM、その他のシリコンディスク */ #define TDK_DISK_FD 0x0014 /* フロッピーディスク */ #define TDK_DISK_HD 0x0015 /* ハードディスク */ #define TDK_DISK_CDROM 0x0016 /* CD-ROM */
ディスク種別の定義は、μT-Kernelの動作には影響しない。これらの定義は、デバイスドライバやアプリケーションにおいて、必要な場合にのみ利用する。たとえば、アプリケーションがデバイスやメディアの種類によって処理内容を変える必要がある場合に、ディスク種別の情報を利用する。特に明確な区別をする必要がないデバイスやメディアに対しては、必ずしもディスク種別を割り当てる必要はない。
デバイスディスクリプタは、デバイスをアクセスするための識別子である。
デバイスディスクリプタは、デバイスをオープンしたときに、μT-Kernel/SMによって正の値(>0)が割り当てられる。
デバイスに対する入出力を要求したときには、その要求の識別子として、リクエストID(>0)が割り当てられる。このリクエストIDにより入出力の完了を待つことができる。
デバイスから入出力するデータはデータ番号により指定する。データは固有データと属性データに大別される。
デバイス固有のデータで、データ番号はデバイスごとに定義される。
ドライバやデバイスの状態の取得や設定、特殊機能などを指定する。
データ番号のいくつかは共通で定義されているが、デバイス独自にも定義できる。詳細は属性データ項を参照。
属性データは大きく次の3つに分類される。
すべてのデバイス(デバイスドライバ)に共通に定義する属性。
同じ種類に分類されるデバイス(デバイスドライバ)に共通に定義する属性。
各デバイス(デバイスドライバ)ごとに独自に定義される属性。
デバイス種別属性およびデバイス個別属性については、デバイスドライバの仕様書で定める。ここでは、共通属性のみ定義する。
共通属性の属性データ番号は -1~-99 の範囲となる。共通属性のデータ番号はすべてのデバイスで共通となるが、すべてのデバイスが必ずしもすべての共通属性に対応しているとは限らない。対応していないデータ番号を指定されたときは、エラー E_PAR とする。
共通属性の定義は、以下の通りである。
#define TDN_EVENT (-1) /* RW:事象通知用メッセージバッファID */ #define TDN_DISKINFO (-2) /* R-:ディスク情報 */ #define TDN_DISPSPEC (-3) /* reserved */ #define TDN_PCMCIAINFO (-4) /* reserved */ #define TDN_DISKINFO_D (-5) /* R-:ディスク情報(64ビットデバイス) */
RW: 読込み(tk_rea_dev)/書込み(tk_wri_dev)可能 |
R-: 読込み(tk_rea_dev)のみ可能 |
事象通知用メッセージバッファID
デバイス事象通知用のメッセージバッファのIDである。
デバイスドライバの起動時には、tk_def_dev によってデバイスの登録を行うが、このAPIのリターンパラメータとしてシステムデフォルトの事象通知用メッセージバッファID(evtmbfid
)が返されるので、その値をデバイスドライバ内で保持し、本属性データの初期値とする。
0が設定されている場合は、デバイス事象通知を行わない。デバイス事象通知については、デバイス事象通知項を参照。
32ビットデバイス・ディスク情報
typedef enum { DiskFmt_STD = 0, /* 標準(HDなど) */ DiskFmt_CDROM = 4 /* CD-ROM 640MB */ } DiskFormat;
typedef struct { DiskFormat format; /* フォーマット形式 */ UW protect:1; /* プロテクト状態 */ UW removable:1; /* 取り外し可否 */ UW rsv:30; /* 予約 (常に0) */ W blocksize; /* ブロックバイト数 */ W blockcont; /* 総ブロック数 */ } DiskInfo;
上記の記載以外のDiskFormatの定義については、デバイスドライバ関連の仕様書を参照のこと。
表示デバイス仕様
DEV_SPECの定義については、デバイスドライバ関連の仕様書を参照のこと。
64ビットデバイス・ディスク情報
typedef struct diskinfo_d { DiskFormat format; /* フォーマット形式 */ BOOL protect:1; /* プロテクト状態 */ BOOL removable:1; /* 取り外し可否 */ UW rsv:30; /* 予約 (0) */ W blocksize; /* ブロックバイト数 */ D blockcont_d; /* 64ビットの総ブロック数 */ } DiskInfo_D;
DiskInfo_DとDiskInfoとの差異は、blockcont
あるいは blockcont_d
の部分の名称およびデータタイプのみである。
μT-Kernel/SMは、DiskInfoとDiskInfo_Dの間の変換は行わない。TDN_DISKINFO も TDN_DISKINFO_D も、デバイスドライバへの要求をそのまま渡すのみである。
ディスクドライバは、TDN_DISKINFO または TDN_DISKINFO_D のいずれか一方、または両方に対応する必要がある。TDN_DISKINFO は可能な限り対応することを推奨する。
ディスク全体の総ブロック数がWに収まらない場合でも、個々の区画のブロック数はWに収まることがある。そのような場合、Wに収まる区画は TDN_DISKINFO に対応し、Wに収まらないもののみ TDN_DISKINFO はエラー(E_PAR)とするような実装が望ましい。また、ブロック数がWに収まる場合であっても、TDN_DISKINFO_D に対応することは望ましいことである。
TDN_DISKINFO_D への対応とデバイスドライバ属性の TDA_DEV_D には直接の依存関係がない。TDN_DISKINFO_D に対応しているからといって TDA_DEV_D 属性のデバイスドライバであるとは限らないし、TDA_DEV_D 属性のデバイスドライバであっても TDN_DISKINFO_D に対応しているとは限らない。
なお、上記の共通属性の定義は、μT-Kernelではなくデバイスドライバの仕様の一部を定めるものであり、μT-Kernelの動作には直接影響しない。また、各デバイスドライバは、共通属性に定義されたすべての機能を実装する必要はない。しかし、共通属性の定義はすべてのデバイスドライバに対して有効であり、各デバイスドライバの仕様は、これらの定義と矛盾のないように定める必要がある。
登録済みのデバイスドライバをアプリケーションやミドルウェアから利用するためには、アプリケーションインタフェースを使用する。アプリケーションインタフェースには下記の関数がある。これらの関数は、タスク独立部およびディスパッチ禁止中、割込み禁止中に呼び出すことはできない(E_CTX)。
ID tk_opn_dev( CONST UB *devnm, UINT omode ) ER tk_cls_dev( ID dd, UINT option ) ID tk_rea_dev( ID dd, W start, void *buf, SZ size, TMO tmout ) ID tk_rea_dev_du( ID dd, D start_d, void *buf, SZ size, TMO_U tmout_u ) ER tk_srea_dev( ID dd, W start, void *buf, SZ size, SZ *asize ) ER tk_srea_dev_d( ID dd, D start_d, void *buf, SZ size, SZ *asize ) ID tk_wri_dev( ID dd, W start, CONST void *buf, SZ size, TMO tmout ) ID tk_wri_dev_du( ID dd, D start_d, CONST void *buf, SZ size, TMO_U tmout_u ) ER tk_swri_dev( ID dd, W start, CONST void *buf, SZ size, SZ *asize ) ER tk_swri_dev_d( ID dd, D start_d, CONST void *buf, SZ size, SZ *asize ) ID tk_wai_dev( ID dd, ID reqid, SZ *asize, ER *ioer, TMO tmout ) ID tk_wai_dev_u( ID dd, ID reqid, SZ *asize, ER *ioer, TMO_U tmout_u ) INT tk_sus_dev( UINT mode ) ID tk_get_dev( ID devid, UB *devnm ) ID tk_ref_dev( CONST UB *devnm, T_RDEV *rdev ) ID tk_oref_dev( ID dd, T_RDEV *rdev ) INT tk_lst_dev( T_LDEV *ldev, INT start, INT ndev ) INT tk_evt_dev( ID devid, INT evttyp, void *evtinf )
devnm
で指定したデバイスを omode
で指定したモードでオープンし、デバイスへのアクセスを準備する。戻値に、デバイスディスクリプタを返す。
omode := (TD_READ || TD_WRITE || TD_UPDATE) | [TD_EXCL || TD_WEXCL || TD_REXCL]
#define TD_READ 0x0001 /* 読込み専用 */ #define TD_WRITE 0x0002 /* 書込み専用 */ #define TD_UPDATE 0x0003 /* 読込みおよび書込み */ #define TD_EXCL 0x0100 /* 排他 */ #define TD_WEXCL 0x0200 /* 排他書込み */ #define TD_REXCL 0x0400 /* 排他読込み */
読込み専用
書込み専用
読込みおよび書込み
アクセスモードを指定する。
TD_READ の場合は、tk_wri_dev は使用できない。
TD_WRITE の場合は、tk_rea_dev は使用できない。
排他
排他書込み
排他読込み
排他モードを指定する。
TD_EXCL は、一切の同時オープンを禁止する。
TD_WEXCL は、書込みモード(TD_WRITE または TD_UPDATE)による同時オープンを禁止する。
TD_REXCL は、読込みモード(TD_READ または TD_UPDATE)による同時オープンを禁止する。
表 1. 同じデバイスを同時にオープンしようとしたときの可否
現在オープンモード | 同時オープンモード | ||||||||||||
排他指定なし | TD_WEXCL | TD_REXCL | TD_EXCL | ||||||||||
R | U | W | R | U | W | R | U | W | R | U | W | ||
排他指定なし | R | ○ | ○ | ○ | ○ | ○ | ○ | × | × | × | × | × | × |
U | ○ | ○ | ○ | × | × | × | × | × | × | × | × | × | |
W | ○ | ○ | ○ | × | × | × | ○ | ○ | ○ | × | × | × | |
TD_WEXCL | R | ○ | × | × | ○ | × | × | × | × | × | × | × | × |
U | ○ | × | × | × | × | × | × | × | × | × | × | × | |
W | ○ | × | × | × | × | × | ○ | × | × | × | × | × | |
TD_REXCL | R | × | × | ○ | × | × | ○ | × | × | × | × | × | × |
U | × | × | ○ | × | × | × | × | × | × | × | × | × | |
W | × | × | ○ | × | × | × | × | × | ○ | × | × | × | |
TD_EXCL | R | × | × | × | × | × | × | × | × | × | × | × | × |
U | × | × | × | × | × | × | × | × | × | × | × | × | |
W | × | × | × | × | × | × | × | × | × | × | × | × |
R = TD_READ W = TD_WRITE U = TD_UPDATE ○ = オープン可 × = オープン不可(E_BUSY)
なお、物理デバイスをオープンした場合、その物理デバイスに属する論理デバイスをすべて同じモードでオープンしたのと同様に扱い、排他オープンの処理が行われる。
dd
のデバイスディスクリプタをクローズする。処理中の要求があった場合は、その処理を中止させてクローズする。
option := [TD_EJECT]
#define TD_EJECT 0x0001 /* メディア排出 */
メディア排出
同一デバイスが他からオープンされていなければ、メディアを排出する。ただし、メディアの排出ができないデバイスでは無視される。
デバイスから固有データまたは属性データの読込みを開始する。読込みを開始するのみで、読込み完了を待たずに呼出元へ戻る。読込みが完了するまで、buf
を保持しなければならない。読込み完了は tk_wai_dev により待つ。読込み開始のための処理にかかる時間はデバイスドライバにより異なる。必ずしも即座に戻るとは限らない。
固有データの場合、start
および size
の単位はデバイスごとに決められる。属性データの場合、start
は属性データ番号、size
はバイト数となり、start
のデータ番号の属性データを読み込む。通常、size
は読み込む属性データのサイズ以上でなければならない。複数の属性データを一度に読み込むことはできない。size
=0を指定した場合、実際の読込みは行わず、現時点で読込み可能なサイズを調べる。
読込みまたは書込みの動作中である場合、新たな要求を受け付けられるか否かはデバイスドライバによる。新たな要求を受け付けられない状態の場合、要求受付待ちとなる。要求受付待ちのタイムアウト時間を tmout
に指定する。tmout
には TMO_POL または TMO_FEVR を指定することもできる。なお、タイムアウトするのは要求受付までである。要求が受け付けられた後にはタイムアウトしない。
TDA_DEV_D や TDA_TMO_U 属性のデバイスドライバに対して、本APIを使用しても構わない。その場合、μT-Kernel/SMの中でパラメータを適切に変換する。たとえば、デバイスドライバの属性が TDA_TMO_U の場合、本APIの tmout
で指定されたミリ秒単位のタイムアウト時間が、マイクロ秒単位の時間に換算された上で、TDA_TMO_U 属性のデバイスドライバに渡される。
以下のすべてのサービスプロファイルが有効に設定されている場合に限り、本APIはサポートされる。
tk_rea_dev のパラメータである start
および tmout
を、64ビットの start_d
および64ビットマイクロ秒単位の tmout_u
としたAPIである。
パラメータが start_d
および tmout_u
となった点を除き、本APIの仕様は tk_rea_dev と同じである。詳細は tk_rea_dev の説明を参照のこと。
対応するデバイスドライバが TDA_DEV_D 属性のデバイスドライバでない場合に、開始位置 start_d
としてWに入りきらない値を指定すると、E_PAR のエラーになる。
また、対応するデバイスドライバが TDA_TMO_U 属性のデバイスドライバでない(マイクロ秒単位に対応していない)場合には、デバイスドライバがマイクロ秒単位のタイムアウトを扱うことができない。この場合は、本APIの tmout_u
で指定されたマイクロ秒単位のタイムアウト時間が、ミリ秒単位の時間に切り上げられた上で、デバイスドライバに渡される。
このように、μT-Kernel/SMの中でパラメータの適切な変換を行うので、アプリケーション側では、デバイスドライバの属性が TDA_DEV_D かどうか、すなわちデバイスドライバが64ビット対応かどうかに関して意識する必要はない。
同期読込み。以下と等価である。
ER tk_srea_dev( ID dd, W start, void *buf, SZ size, SZ *asize ) { ER er, ioer; er = tk_rea_dev(dd, start, buf, size, TMO_FEVR); if ( er > 0 ) { er = tk_wai_dev(dd, er, asize, &ioer, TMO_FEVR); if ( er > 0 ) er = ioer; } return er; }
TDA_DEV_D 属性のデバイスドライバに対して、本APIを使用しても構わない。その場合、μT-Kernel/SMの中でパラメータを適切に変換する。
tk_srea_dev のパラメータである start
を、64ビットの start_d
としたAPIである。
パラメータが start_d
となった点を除き、本APIの仕様は tk_srea_dev と同じである。詳細は tk_srea_dev の説明を参照のこと。
対応するデバイスドライバが TDA_DEV_D 属性のデバイスドライバでない場合に、開始位置 start_d
としてWに入りきらない値を指定すると、E_PAR のエラーになる。
このように、μT-Kernel/SMの中でパラメータの適切な変換を行うので、アプリケーション側では、デバイスドライバの属性が TDA_DEV_D かどうか、すなわちデバイスドライバが64ビット対応かどうかに関して意識する必要はない。
デバイスへ固有データまたは属性データの書込みを開始する。書込みを開始するのみで、書込み完了を待たずに呼出元へ戻る。書込みが完了するまで、buf
を保持しなければならない。書込み完了は tk_wai_dev により待つ。書込み開始のための処理にかかる時間はデバイスドライバにより異なる。必ずしも即座に戻るとは限らない。
固有データの場合、start
および size
の単位はデバイスごとに決められる。属性データの場合、start
は属性データ番号、size
はバイト数となり、start
のデータ番号の属性データに書き込む。通常、size
は書き込む属性データのサイズと同じでなければならない。複数の属性データを一度に書き込むことはできない。size
=0を指定した場合、実際の書込みは行わず、現時点で書込み可能なサイズを調べる。
読込みまたは書込みの動作中である場合、新たな要求を受け付けられるか否かはデバイスドライバによる。新たな要求を受け付けられない状態の場合、要求受付待ちとなる。要求受付待ちのタイムアウト時間を tmout
に指定する。tmout
には TMO_POL または TMO_FEVR を指定することもできる。なお、タイムアウトするのは要求受付までである。要求が受け付けられた後にはタイムアウトしない。
TDA_DEV_D や TDA_TMO_U 属性のデバイスドライバに対して、本APIを使用しても構わない。その場合、μT-Kernel/SMの中でパラメータを適切に変換する。たとえば、デバイスドライバの属性が TDA_TMO_U の場合、本APIの tmout
で指定されたミリ秒単位のタイムアウト時間が、マイクロ秒単位の時間に換算された上で、TDA_TMO_U 属性のデバイスドライバに渡される。
以下のすべてのサービスプロファイルが有効に設定されている場合に限り、本APIはサポートされる。
tk_wri_dev のパラメータである start
および tmout
を、64ビットの start_d
および64ビットマイクロ秒単位の tmout_u
としたAPIである。
パラメータが start_d
および tmout_u
となった点を除き、本APIの仕様は tk_wri_dev と同じである。詳細は tk_wri_dev の説明を参照のこと。
対応するデバイスドライバが TDA_DEV_D 属性のデバイスドライバでない場合に、開始位置 start_d
としてWに入りきらない値を指定すると、E_PAR のエラーになる。
また、対応するデバイスドライバが TDA_TMO_U 属性のデバイスドライバでない(マイクロ秒単位に対応していない)場合には、デバイスドライバがマイクロ秒単位のタイムアウトを扱うことができない。この場合は、本APIの tmout_u
で指定されたマイクロ秒単位のタイムアウト時間が、ミリ秒単位の時間に切り上げられた上で、デバイスドライバに渡される。
このように、μT-Kernel/SMの中でパラメータの適切な変換を行うので、アプリケーション側では、デバイスドライバの属性が TDA_DEV_D かどうか、すなわちデバイスドライバが64ビット対応かどうかに関して意識する必要はない。
同期書込み。以下と等価である。
ER tk_swri_dev( ID dd, W start, void *buf, SZ size, SZ *asize ) { ER er, ioer; er = tk_wri_dev(dd, start, buf, size, TMO_FEVR); if ( er > 0 ) { er = tk_wai_dev(dd, er, asize, &ioer, TMO_FEVR); if ( er > 0 ) er = ioer; } return er; }
TDA_DEV_D 属性のデバイスドライバに対して、本APIを使用しても構わない。その場合、μT-Kernel/SMの中でパラメータを適切に変換する。
tk_swri_dev のパラメータである start
を、64ビットの start_d
としたAPIである。
パラメータが start_d
となった点を除き、本APIの仕様は tk_swri_dev と同じである。詳細は tk_swri_dev の説明を参照のこと。
対応するデバイスドライバが TDA_DEV_D 属性のデバイスドライバでない場合に、開始位置 start_d
としてWに入りきらない値を指定すると、E_PAR のエラーになる。
このように、μT-Kernel/SMの中でパラメータの適切な変換を行うので、アプリケーション側では、デバイスドライバの属性が TDA_DEV_D かどうか、すなわちデバイスドライバが64ビット対応かどうかに関して意識する必要はない。
dd
に対する reqid
の要求の完了を待つ。reqid
=0 の場合は、dd
に対する要求の内のいずれかが完了するのを待つ。なお、現時点で処理中の要求のみが完了待ちの対象となる。tk_wai_dev の呼出後に要求された処理は完了待ちの対象とならない。
複数の要求を同時に処理している場合、その要求の完了の順序は必ずしも要求した順序ではなく、デバイスドライバに依存する。ただし、要求した順序で処理した場合と結果が矛盾しないような順序で処理されることは保証される。例えば、ディスクからの読込みの場合、次のような処理順序の変更が考えられる。
1 4 3 2 5
1 2 3 4 5
このように順序を入れ替えて処理することにより、シークや回転待ちを減らすことができ、より効率的にディスクアクセスができる。
tmout
に完了待ちのタイムアウト時間を指定する。TMO_POL または TMO_FEVR を指定することもできる。タイムアウト(E_TMOUT)した場合は要求された処理を継続中なので、再度 tk_wai_dev により完了を待つ必要がある。reqid
> 0かつ tmout
=TMO_FEVR の場合はタイムアウトすることはなく、必ず処理が完了する。
要求された処理に対して、デバイスドライバから処理結果のエラー(入出力エラーなど)が返った場合、そのエラーコードは、戻値ではなく ioer
に格納される。具体的には、tk_wai_dev の処理のために呼び出された完了待ち関数 waitfn の中で、要求パケットT_DEVREQの error
に格納されたエラーコードが、処理結果のエラーとして ioer
に返される。
一方、戻値には、要求の完了待ちが正しくできなかった場合にエラーを返す。戻値にエラーが返された場合、ioer
の内容は無意味である。また、戻値にエラーが返された場合は処理を継続中なので、再度 tk_wai_dev により完了を待つ必要がある。詳細は「waitfn - 完了待ち関数項」を参照のこと。
tk_wai_dev で完了待ち中にタスク例外が発生すると、reqid
の要求を中止して処理を完了させる。中止した処理の結果がどのようになるかは、デバイスドライバに依存する。ただし、reqid
=0の場合は、要求を中止することなくタイムアウトと同様に扱われる。この場合は、E_TMOUT ではなく E_ABORT を返す。
同じリクエストIDに対して、複数のタスクから同時に完了待ちすることはできない。reqid
=0で待っているタスクがあれば、同じ dd
に対して他のタスクは完了待ちできない。同様に、reqid
> 0で待っているタスクがあれば、他のタスクで reqid
=0の完了待ちはできない。
TDA_TMO_U 属性のデバイスドライバに対して、本APIを使用しても構わない。その場合、μT-Kernel/SMの中でパラメータを適切に変換する。たとえば、デバイスドライバの属性が TDA_TMO_U の場合、本APIの tmout
で指定されたミリ秒単位のタイムアウト時間が、マイクロ秒単位の時間に換算された上で、TDA_TMO_U 属性のデバイスドライバに渡される。
tk_wai_dev のパラメータである tmout
を、64ビットマイクロ秒単位の tmout_u
としたAPIである。
パラメータが tmout_u
となった点を除き、本APIの仕様は tk_wai_dev と同じである。詳細は tk_wai_dev の説明を参照のこと。
対応するデバイスドライバが TDA_TMO_U 属性のデバイスドライバでない(マイクロ秒単位に対応していない)場合には、デバイスドライバがマイクロ秒単位のタイムアウトを扱うことができない。この場合は、本APIの tmout_u
で指定されたマイクロ秒単位のタイムアウト時間が、ミリ秒単位の時間に切り上げられた上で、デバイスドライバに渡される。
このように、μT-Kernel/SMの中でパラメータの適切な変換を行うので、アプリケーション側では、デバイスドライバの属性が TDA_TMO_U かどうか、すなわちデバイスドライバがマイクロ秒単位に対応かどうかに関して意識する必要はない。
mode
にしたがって処理を行い、その処理を行ったあとのサスペンド禁止要求カウント数を戻値に返す。
mode := ( (TD_SUSPEND | [TD_FORCE]) || TD_DISSUS || TD_ENASUS || TD_CHECK)
#define TD_SUSPEND 0x0001 /* サスペンド */ #define TD_DISSUS 0x0002 /* サスペンドを禁止 */ #define TD_ENASUS 0x0003 /* サスペンドを許可 */ #define TD_CHECK 0x0004 /* サスペンド禁止要求カウント取得 */ #define TD_FORCE 0x8000 /* 強制サスペンド指定 */
サスペンド
サスペンド許可状態であればサスペンドする。
サスペンド禁止状態であれば E_BUSY を返す。
強制サスペンド
サスペンド禁止状態であってもサスペンドする。
サスペンド禁止
サスペンドを禁止する。
サスペンド許可
サスペンドを許可する。
サスペンド禁止カウント取得
サスペンド禁止要求を行っている回数の取得のみ行う。
サスペンドは、次の手順によって行われる。
各サブシステムのサスペンド開始前の処理
tk_evt_ssy(0, TSEVT_SUSPEND_BEGIN, 0)
各デバイスのサスペンド処理
各サブシステムのサスペンド完了後の処理
tk_evt_ssy(0, TSEVT_SUSPEND_DONE, 0)
サスペンド状態へ移行
tk_set_pow(TPW_DOSUSPEND)
リジューム(サスペンドからの復帰)は、次の手順によって行われる。
サスペンド状態から復帰
tk_set_pow(TPW_DOSUSPEND) から戻る
各サブシステムのリジューム開始前の処理
tk_evt_ssy(0, TSEVT_RESUME_BEGIN, 0)
各デバイスのリジューム処理
各サブシステムのリジューム完了後の処理
tk_evt_ssy(0, TSEVT_RESUME_DONE, 0)
サスペンド禁止は、その要求回数がカウントされる。同じ回数だけサスペンド許可を要求しないとサスペンドは許可されない。システム起動時はサスペンド許可(サスペンド禁止要求カウント=0)である。サスペンド禁止要求カウントの上限は実装依存だが、最低255回まではカウントできるものとする。上限を超えた場合は E_QOVR を返す。
devid
で示すデバイスのデバイス名を取得し devnm
に格納する。
devid
は物理デバイスのデバイスIDまたは論理デバイスのデバイスIDである。
devid
が物理デバイスであれば、devnm
には物理デバイス名が格納される。
devid
が論理デバイスであれば、devnm
には論理デバイス名が格納される。
devnm
は L_DEVNM+1 バイト以上の領域が必要である。
戻値には、devid
のデバイスが属する物理デバイスのデバイスIDを返す。
rdev
の内容
devnm
で示すデバイスのデバイス情報を取得し、rdev
に格納する。rdev
=NULL とした場合には、デバイス情報は格納されない。
nsub
は、devnm
で示すデバイスが属する物理デバイスのサブユニット数である。
戻値に devnm
のデバイスのデバイスIDを返す。
rdev
の内容
dd
で示すデバイスのデバイス情報を取得し、rdev
に格納する。rdev
=NULL とした場合には、デバイス情報は格納されない。
nsub
は、dd
で示すデバイスが属する物理デバイスのサブユニット数である。
戻値に dd
のデバイスのデバイスIDを返す。
ldev
の内容
登録済みのデバイスの情報を取得する。登録デバイスは物理デバイス単位で管理される。したがって、登録デバイス情報も物理デバイス単位で取得される。
登録デバイスの数がNの時、登録デバイスに0~N-1の連番を振る。この連番にしたがって、start
番目から ndev
個の登録情報を取得し、ldev
に格納する。ldev
は ndev
個の情報を格納するのに十分な大きさの領域でなければならない。戻値には、start
以降の残りの登録数(N-start
)を返す。
start
以降の残りが ndev
個に満たない場合は、残りのすべてを格納する。戻値≦ndev
であれば、すべての登録情報が取得できたことを示す。なお、この連番はデバイスの登録・抹消があると変化する。したがって、複数回に分けて取得すると、正確な情報が得られない場合がある。
devid
のデバイス(デバイスドライバ)に、ドライバ要求イベントを送信する。
ドライバ要求イベントの機能(処理内容)および evtinf
の内容はイベントタイプごとに定義される。ドライバ要求イベントについては、「eventfn - イベント関数項」を参照のこと。
デバイスドライバは、物理デバイスごとに登録する。
idev
の内容
devnm
のデバイス名でデバイス(デバイスドライバ)を登録し、登録したデバイスのデバイスIDを戻値に返す。devnm
のデバイスがすでに登録されているときは、新しい登録情報で更新する。更新の場合は、デバイスIDは変更されない。
ddev
でデバイス登録情報を指定する。ddev
=NULL の場合は、devnm
のデバイス登録を抹消する。
ddev
は次の形式の構造体である。
typedef struct t_ddev { void *exinf; /* 拡張情報 */ ATR drvatr; /* ドライバ属性 */ ATR devatr; /* デバイス属性 */ INT nsub; /* サブユニット数 */ SZ blksz; /* 固有データのブロックサイズ (-1:不明) */ FP openfn; /* オープン関数 */ FP closefn; /* クローズ関数 */ FP execfn; /* 処理開始関数 */ FP waitfn; /* 完了待ち関数 */ FP abortfn; /* 中止処理関数 */ FP eventfn; /* イベント関数 */ /* 以下に実装独自の情報が追加される場合がある */ } T_DDEV;
exinf
は、任意の情報の格納に使用できる。この値は、各処理関数に渡される。内容に関してデバイス管理では関知しない。
drvatr
は、デバイスドライバの属性に関する情報を設定する。下位側がシステム属性を表し、上位側が実装独自属性を表す。実装独自属性は、T_DDEVに実装独自データを追加する場合に有効フラグを定義するためなどに使用する。
drvatr := [TDA_OPENREQ] | [TDA_TMO_U] | [TDA_DEV_D]
#define TDA_OPENREQ 0x0001 /* 毎回オープン/クローズ */ #define TDA_TMO_U 0x0002 /* マイクロ秒単位タイムアウト時間使用 */ #define TDA_DEV_D 0x0004 /* 64ビットデバイス */
drvatr
では、以下のドライバ属性を組み合わせて指定することができる。
TDA_OPENREQ
デバイスが多重オープンされた場合、通常は最初のオープン時に openfn が呼び出され、最後のクローズ時に closefn が呼び出される。TDA_OPENREQ を指定した場合、多重オープンの場合でもすべてのオープン/クローズ時に openfn/closefn が呼び出される。
TDA_TMO_U
マイクロ秒単位タイムアウト時間を使用することを示す。
この場合、ドライバ処理関数のタイムアウト指定 tmout
がTMO_U型(マイクロ秒単位)となる。
TDA_DEV_D
64ビットデバイスを使用することを示す。この場合、ドライバ処理関数の要求パケット devreq
の型がT_DEVREQ_Dとなる。
TDA_TMO_U および TDA_DEV_D を指定した場合、ドライバ処理関数の一部の引数の型が変化する。このような引数の型を変化させるドライバ属性を複数組み合わせて指定した場合は、指定されたすべての型変化を行った引数を持つドライバ処理関数となる。
devatr
は、デバイス属性を設定する。デバイス属性の詳細については前述。
nsub
は、サブユニット数を設定する。サブユニットがない場合は0とする。
blksz
は、固有データのブロックサイズをバイト数で設定する。ディスクデバイスの場合は、物理ブロックサイズとなる。シリアル回線などは1バイトとなる。固有データのないデバイスでは0とする。未フォーマットのディスクなど、ブロックサイズが不明の場合は-1とする。blksz
≦0の場合は、固有データにアクセスできない。tk_rea_dev, tk_wri_dev で固有データをアクセスする場合に、size
* blksz
がアクセスする領域サイズ、つまり buf
のサイズとならなければならない。
openfn, closefn, execfn, waitfn, abortfn, eventfn は、ドライバ処理関数のエントリーアドレスを設定する。ドライバ処理関数の詳細については、「デバイスドライバインタフェース項」を参照。
idev
にデバイス初期情報が返される。ここには、デバイスドライバ起動時のデフォルトとして設定するための情報などが返されるので、必要に応じて利用する。idev
=NULL とした場合には、デバイス初期情報は格納されない。
evtmbfid
は、システムデフォルトの事象通知用メッセージバッファIDである。システムデフォルトの事象通知用メッセージバッファがない場合は0が設定される。
デバイス登録および抹消が行われたとき、各サブシステムに対して次のように通知が行われる。devid
は登録・抹消された、物理デバイスのデバイスIDである。
tk_evt_ssy(0, TSEVT_DEVICE_REGIST, devid
)
tk_evt_ssy(0, TSEVT_DEVICE_DELETE, devid
)
idev
の内容
デバイス初期情報を取得する。tk_def_dev で得られるものと同じ内容である。
E_MACV のエラーは、多くのシステムコールで共通に発生する可能性があり、通常はシステムコール別のエラーコードには記載していない。しかし、本APIの場合は代表的なエラーが E_MACV のみであるため、エラーコード欄にこのエラーを記載している。
デバイスドライバインタフェースは、デバイス登録時に指定した処理関数(ドライバ処理関数)群によって構成される。
ER openfn(ID devid
, UINT omode
, void *exinf
);
ER closefn(ID devid
, UINT option
, void *exinf
);
ER execfn(T_DEVREQ *devreq
, TMO tmout
, void *exinf
);
INT waitfn(T_DEVREQ *devreq
, INT nreq
, TMO tmout
, void *exinf
);
ER abortfn(ID tskid
, T_DEVREQ *devreq
, INT nreq
, void *exinf
);
INT eventfn(INT evttyp
, void *evtinf
, void *exinf
);
ドライバ属性に TDA_TMO_U を指定した場合、次のドライバ処理関数のタイムアウト指定 tmout
がTMO_U型(マイクロ秒単位)となる。
ドライバ属性に TDA_DEV_D を指定した場合、次のドライバ処理関数の要求パケット devreq
の型がT_DEVREQ_Dとなる。
ドライバ属性に TDA_TMO_U と TDA_DEV_D を指定した場合、指定されたすべての型変化を行った引数を持つドライバ処理関数となる。
ドライバ処理関数は、デバイス管理によって呼び出され、準タスク部として実行される。これらのドライバ処理関数は、再入可能(reentrant)でなければならない。また、ドライバ処理関数が排他的に呼び出されることは保証されない。例えば、複数のタスクから同じデバイスに対して同時に要求があった場合、それぞれのタスクが同時にドライバ処理関数を呼び出すことがある。デバイスドライバ側は、必要に応じて排他制御などを行う必要がある。
デバイスドライバへの入出力要求は、リクエストIDに対応した下記の要求パケットにより行う。
/* * デバイス要求パケット: 32ビット用 * In: ドライバ処理関数への入力パラメータ(μT-Kernel/SMのデバイス管理で設定) * Out: ドライバ処理関数からの出力パラメータ(ドライバ処理関数で設定) * X: 上記以外のパラメータ */ typedef struct t_devreq { struct t_devreq *next; /* In:要求パケットのリンク (NULL:終端) */ void *exinf; /* X:拡張情報 */ ID devid; /* In:対象デバイスID */ INT cmd:4; /* In:要求コマンド */ BOOL abort:1; /* In:中止要求を行った時 TRUE */ W start; /* In:開始データ番号 */ SZ size; /* In:要求サイズ */ void *buf; /* In:入出力バッファアドレス */ SZ asize; /* Out:結果サイズ */ ER error; /* Out:結果エラー */ /* 以下に実装独自の情報が追加される場合がある */ } T_DEVREQ;
/* * デバイス要求パケット: 64ビット用 * In: ドライバ処理関数への入力パラメータ(μT-Kernel/SMのデバイス管理で設定) * Out: ドライバ処理関数からの出力パラメータ(ドライバ処理関数で設定) * X: 上記以外のパラメータ */ typedef struct t_devreq_d { struct t_devreq_d *next; /* In:要求パケットのリンク (NULL:終端) */ void *exinf; /* X:拡張情報 */ ID devid; /* In:対象デバイスID */ INT cmd:4; /* In:要求コマンド */ BOOL abort:1; /* In:中止要求を行った時 TRUE */ D start_d; /* In:開始データ番号, 64ビット */ SZ size; /* In:要求サイズ */ void *buf; /* In:入出力バッファアドレス */ SZ asize; /* Out:結果サイズ */ ER error; /* Out:結果エラー */ /* 以下に実装独自の情報が追加される場合がある */ } T_DEVREQ_D;
In: はドライバ処理関数への入力パラメータであり、μT-Kernel/SMのデバイス管理で設定され、デバイスドライバ側で変更してはならない。入力パラメータ(In:)以外は、デバイス管理により最初に0クリアされる。その後はデバイス管理は変更しない。Out: はドライバ処理関数から戻る際の出力パラメータであり、ドライバ処理関数の中で設定する。
next
は、要求パケットをリンクするために使用する。デバイス管理内の要求パケットの管理に使用される他、完了待ち関数(waitfn),中止処理関数(abortfn)でも使用する。
exinf
は、デバイスドライバ側で任意に使用できる。デバイス管理では内容には関知しない。
devid
は、要求対象のデバイスIDが設定される。
cmd
は、要求コマンドが設定される。
cmd := (TDC_READ || TDC_WRITE)
#define TDC_READ 1 /* 読込み要求 */ #define TDC_WRITE 2 /* 書込み要求 */
abort
は中止処理を行う場合、中止処理関数(abortfn)を呼び出す直前に TRUE を設定する。abort
は中止処理を要求したことを示すフラグであり、処理が中止されたことを示すものではない。また、中止処理関数(abortfn)を呼び出さない場合でも abort
が TRUE に設定される場合がある。abort
が TRUE に設定された要求がデバイスドライバに渡された場合は、中止処理を行う。
start
, start_d
, size
は、tk_rea_dev, tk_rea_dev_du, tk_wri_dev, tk_wri_dev_du で指定された start
, start_d
, size
がそのまま設定される。
buf
は、tk_rea_dev, tk_rea_dev_du, tk_wri_dev, tk_wri_dev_du で指定された buf
がそのまま設定される。仮想記憶をサポートするシステムでは、bufの指す領域が非常駐である場合や、タスク固有空間の場合があるため、取り扱いに注意が必要である。
asize
は、tk_wai_dev の asize
に返す値をデバイスドライバが設定する。
error
は、tk_wai_dev の戻値として返すエラーコードをデバイスドライバが設定する。正常であれば E_OK を設定する。
T_DEVREQとT_DEVREQ_Dとの差異は、start
あるいは start_d
の部分の名称およびデータタイプのみである。
デバイス要求パケットの種類(T_DEVREQまたはT_DEVREQ_D)は、デバイス登録時のドライバ属性 TDA_DEV_D によって選択される。したがって、1つのドライバに対する要求パケットにT_DEVREQとT_DEVREQ_Dが混在することはない。
ID |
devid
| Device ID | オープンするデバイスのデバイスID |
UINT |
omode
| Open Mode | オープンモード(tk_opn_dev と同じ) |
void* |
exinf
| Extended Information | デバイス登録時に指定した拡張情報 |
tk_opn_dev が呼び出されたときに、オープン関数 openfn が呼び出される。
openfn では、デバイスの使用開始のための処理を行う。処理内容はデバイスに依存し、何もする必要がなければ何もしなくてもよい。また、オープンされているか否かをデバイスドライバで記憶する必要もなく、オープンされていない(openfn が呼び出されていない)という理由だけで、他の処理関数が呼び出されたときエラーにする必要もない。オープンされていない状態で他の処理関数が呼び出された場合でも、デバイスドライバの動作に問題がなければ要求を処理してしまって構わない。
openfn でデバイスの初期化等を行う場合でも、待ちを伴うような処理は原則として行わない。できる限り速やかに処理を行い openfn から戻らなければならない。例えば、シリアル回線のように通信モードを設定する必要があるようなデバイスでは、tk_wri_dev により通信モードが設定されたときにデバイスの初期化を行えばよい。openfn ではデバイスの初期化を行う必要はない。
同じデバイスが多重オープンされた場合、通常は最初のオープン時のみ呼び出されるが、デバイス登録時にドライバ属性として TDA_OPENREQ が指定された場合は、すべてのオープン時に呼び出される。
多重オープンやオープンモードに関する処理はデバイス管理で行われるため、openfn ではそれらに関する処理は特に必要ない。omode
も参考情報として渡されるだけで、omode
に関する処理を行う必要はない。
openfn は、tk_opn_dev の発行タスクの準タスク部として実行される。すなわち、tk_opn_dev の発行タスクを要求タスクとする準タスク部のコンテキストで実行される。
ID |
devid
| Device ID | クローズするデバイスのデバイスID |
UINT |
option
| Close Option | クローズオプション(tk_cls_dev と同じ) |
void* |
exinf
| Extended Information | デバイス登録時に指定した拡張情報 |
tk_cls_dev が呼び出されたときに、クローズ関数 closefn が呼び出される。
closefn では、デバイスの使用終了のための処理を行う。処理内容はデバイスに依存し、何もする必要がなければ何もしなくてもよい。
メディアの取り外しが可能なデバイスの場合、option
に TD_EJECT が指定されていたならメディアの排出を行う。
closefn でデバイスの終了処理やメディアの排出を行う場合でも、待ちを伴うような処理は原則として行わない。できる限り速やかに処理を行い closefn から戻らなければならない。メディアの排出に時間がかかる場合、排出の完了を待たずに closefn から戻っても構わない。
同じデバイスが多重オープンされていた場合、通常は最後のクローズ時のみ呼び出されるが、デバイス登録時にドライバ属性として TDA_OPENREQ が指定された場合は、すべてのクローズ時に呼び出される。ただし、この場合も最後のクローズにしか option
に TD_EJECT が指定されることはない。
多重オープンやオープンモードに関する処理はデバイス管理で行われるため、closefn ではそれらに関する処理は特に必要ない。
closefn は、tk_cls_dev の発行タスクの準タスク部として実行される。
tk_rea_dev または tk_wri_dev が呼び出されたときに、処理開始関数 execfn が呼び出される。
devreq
の要求の処理を開始する。処理を開始するのみで、完了を待たずに呼出元へ戻る。処理開始のためにかかる時間はデバイスドライバに依存する。必ずしも即座に完了するとは限らない。
新たな要求を受け付けられない状態のときは、要求受付待ちとなる。tmout
に指定した時間以内に新たな要求を受け付けられなければ、タイムアウトする。tmout
には、TMO_POL または TMO_FEVR を指定することもできる。タイムアウトした場合、execfn の戻値に E_TMOUT を返す。要求パケットの error
は変更しない。タイムアウトするのは要求を受け付けるまでで、要求を受け付けた後はタイムアウトしない。
execfn の戻値にエラーを返した場合は、要求が受け付けられなかったものとして、要求パケットは消滅する。
処理を中止する場合、その要求の受け付け前(処理開始前)であれば execfn の戻値に E_ABORT を返す。この場合、要求パケットは消滅する。要求受け付け後(処理開始後)の場合は、E_OK を返す。この場合、要求パケットは waitfn を実行し処理完了が確認されるまで消滅しない。
中止要求があった場合、execfn からできる限り速やかに戻らなければならない。処理を中止しなくてもすぐに終るのであれば中止しなくてもよい。
execfn は、tk_rea_dev, tk_wri_dev, tk_srea_dev, tk_swri_dev の発行タスクの準タスク部として実行される。
デバイス登録時のドライバ属性として TDA_DEV_D が指定されたデバイスドライバにおいて、tk_rea_dev または tk_wri_dev が呼び出されたときに、処理開始関数(64ビット要求パケット、ミリ秒タイムアウト) execfn が呼び出される。この場合、パラメータの要求パケットが64ビットのT_DEVREQ_D* devreq_d
となった点を除き、関数の仕様は32ビット要求パケット、ミリ秒タイムアウトの execfn と同じである。
デバイス登録時のドライバ属性として TDA_TMO_U が指定されたデバイスドライバにおいて、tk_rea_dev または tk_wri_dev が呼び出されたときに、処理開始関数(32ビット要求パケット、マイクロ秒タイムアウト) execfn が呼び出される。この場合、パラメータのタイムアウト指定がマイクロ秒単位のTMO_U tmout_u
となった点を除き、関数の仕様は32ビット要求パケット、ミリ秒タイムアウトの execfn と同じである。
デバイス登録時のドライバ属性として TDA_DEV_D および TDA_TMO_U が指定されたデバイスドライバにおいて、tk_rea_dev または tk_wri_dev が呼び出されたときに、処理開始関数(64ビット要求パケット、マイクロ秒タイムアウト) execfn が呼び出される。この場合、パラメータの要求パケットが64ビットのT_DEVREQ_D* devreq_d
、パラメータのタイムアウト指定がマイクロ秒単位のTMO_U tmout_u
となった点を除き、関数の仕様は32ビット要求パケット、ミリ秒タイムアウトの execfn と同じである。
/* 完了待ち関数(32ビット要求パケット、ミリ秒タイムアウト) */
INT creqno = waitfn
(T_DEVREQ *devreq, INT nreq, TMO tmout, void *exinf);
/* 完了待ち関数(64ビット要求パケット、ミリ秒タイムアウト) */
INT creqno = waitfn
(T_DEVREQ_D *devreq_d, INT nreq, TMO tmout, void *exinf);
tk_wai_dev が呼び出されたときに、完了待ち関数 waitfn が呼び出される。
devreq
は devreq
->next
で接続された要求パケットのリストで、devreq
から nreq
個分の要求パケットについて、その内のいずれかが完了するのを待つ。リストの最後の next
は必ずしも NULL になっているとは限らないため、必ず nreq
の指定に従う。戻値に完了した要求パケットの番号(devreq
から何番目か)を返す。最初が0番目で最後が nreq
-1番目となる。なお、完了とは、正常終了/異常(エラー)終了/中止のいずれかである。
tmout
に完了待ちのタイムアウト時間を指定する。TMO_POL または TMO_FEVR を指定することもできる。タイムアウトした場合は、要求された処理は継続中である。タイムアウトの場合、waitfn の戻値に E_TMOUT を返す。要求パケットの error
は変更しない。なお、要求された処理を継続中で waitfn から戻るときは、waitfn の戻値に必ずエラーを返す。戻値にエラーを返したにもかかわらず処理が完了していてはいけないし、処理継続中であればエラー以外を返してもいけない。waitfn の戻値にエラーが返されている限り、その要求は処理中として要求パケットは消滅しない。waitfn の戻値に処理を完了した要求パケットの番号が返されたとき、その要求の処理は完了したものとして要求パケットは消滅する。
入出力エラーなどデバイスに関するエラーを、要求パケットの error
に格納する。waitfn の戻値には、完了待ちが正しくできなかった場合のエラーを返す。waitfn の戻値が tk_wai_dev の戻値となり、要求パケットの error
が ioer
に戻される。
waitfn による完了待ちの間に中止処理関数 abortfn が実行された時の中止の処理は、単一要求の完了待ちだった場合(waitfn の nreq
=1)と、複数要求の完了待ちだった場合(waitfn の nreq
> 1)で異なっている。単一要求の完了待ちだった場合は、処理中の要求を中止する。一方、複数要求の完了待ちだった場合は、特別な扱いとして、waitfn による待ちの解除のみを行い、要求に対する処理自体は中止しない。すなわち、中止処理関数 abortfn が実行されても、要求パケットの abort
は FALSE のままであり、要求に対する処理は継続する。待ち解除となった waitfn からは、戻値に E_ABORT を返す。
完了待ち要求の中には、中止要求が要求パケットの abort
にセットされている場合がある。このような場合、単一要求の完了待ちではその要求の中止処理を行わなければならない。複数要求の完了待ちでも中止処理を行うのが望ましいが、abort
フラグを無視しても構わない。
なお、中止処理では waitfn からできる限り速やかに戻ることが重要であり、処理を中止しなくてもすぐに処理が終るのであれば中止しなくてもよい。
処理が中止された場合は、要求パケットの error
に E_ABORT を返すことを原則とするが、そのデバイスの特性に合わせて E_ABORT 以外のエラーを返してもよい。また、中止される直前までの処理を有効として E_OK としてもよい。なお、中止要求があっても正常に最後まで処理したのなら E_OK を返す。
waitfn は、tk_wai_dev, tk_srea_dev, tk_swri_dev の発行タスクの準タスク部として実行される。
デバイス登録時のドライバ属性として TDA_DEV_D が指定されたデバイスドライバにおいて、tk_wai_dev が呼び出されたときに、完了待ち関数(64ビット要求パケット、ミリ秒タイムアウト) waitfn が呼び出される。この場合、パラメータの要求パケットが64ビットのT_DEVREQ_D* devreq_d
となった点を除き、関数の仕様は32ビット要求パケット、ミリ秒タイムアウトの waitfn と同じである。
デバイス登録時のドライバ属性として TDA_TMO_U が指定されたデバイスドライバにおいて、tk_wai_dev が呼び出されたときに、完了待ち関数(32ビット要求パケット、マイクロ秒タイムアウト) waitfn が呼び出される。この場合、パラメータのタイムアウト指定がマイクロ秒単位のTMO_U tmout_u
となった点を除き、関数の仕様は32ビット要求パケット、ミリ秒タイムアウトの waitfn と同じである。
デバイス登録時のドライバ属性として TDA_DEV_D および TDA_TMO_U が指定されたデバイスドライバにおいて、tk_wai_dev が呼び出されたときに、完了待ち関数(64ビット要求パケット、マイクロ秒タイムアウト) waitfn が呼び出される。この場合、パラメータの要求パケットが64ビットのT_DEVREQ_D* devreq_d
、パラメータのタイムアウト指定がマイクロ秒単位のTMO_U tmout_u
となった点を除き、関数の仕様は32ビット要求パケット、ミリ秒タイムアウトの waitfn と同じである。
現在実行中の処理開始関数 execfn や完了待ち関数 waitfn から速やかに戻らせたいときに、中止処理関数 abortfn が呼び出される。通常は、処理中の要求を中止して戻らせる。ただし、中止しなくてもすぐに処理が終るのであれば、必ずしも中止しなくてもよい。重要なのは、できる限り速やかに execfn, waitfn から戻ることである。
abortfn は、次のような場合に呼び出される。
タスク例外の発生によるブレーク関数の実行時に、タスク例外の発生したタスクによる処理中の要求があった場合、そのタスクによる処理中の要求を中止する。
tk_cls_dev によるデバイスクローズ時に、クローズするデバイスディスクリプタによる処理中の要求があった場合、そのデバイスディスクリプタによる処理中の要求を中止する。
tskid
は、devreq
で指定した要求を実行中のタスクである。つまり、execfn, waitfn を実行しているタスクである。devreq
, nreq
は、execfn, waitfn の引数として指定したものと同じである。ただし、execfn の場合は常に nreq
=1である。
abortfn は、execfn, waitfn を実行しているタスクとは別のタスクから呼び出される。両者は並行して実行されるため、必要に応じて排他制御等を行う必要がある。また、execfn, waitfn の呼出直前や execfn, waitfn から戻る途中に abortfn が呼び出される可能性もある。このような場合においても正しく動作するように配慮する必要がある。abortfn を呼び出す前に、中止処理対象の要求パケットの abort
フラグに TRUE が設定されるので、execfn, waitfn はこれにより中止要求の有無を知ることもできる。また、abortfn では、任意のオブジェクトに対する tk_dis_wai を使用することができる。
複数要求待ち(nreq
> 1)の waitfn 実行中の場合は、特別な扱いとなり他の場合とは次の点で異なる。
要求の処理を中止することはせず、完了待ちのみ中止(待ちを解除)する。
要求パケットの abort
フラグはセットされない(abort
=FALSE のまま)。
なお、execfn, waitfn の実行中でないときに要求を中止させる場合には、abortfn を呼び出すことなく、要求パケットの abort
フラグがセットされる。abort
フラグがセットされた状態で execfn が呼び出されたときは、要求を受け付けない。waitfn が呼び出されたときは、abortfn が呼び出された場合と同様の中止処理を行う。
execfn により処理を開始した要求が、waitfn による完了待ちでない状態で中止された場合は、後で waitfn が呼び出されたときに中止されて処理が完了したことを知らせる。処理が中止されても、waitfn により完了が確認されるまでは要求自体は消滅しない。
abortfn は中止処理を開始するのみで、中止が完了するまで待たずに速やかに戻る。
タスク例外の際に実行される abortfn は、タスク例外を発生させた tk_ras_tex の発行タスクの準タスク部として実行される。また、デバイスクローズ時に実行される abortfn は、tk_cls_dev の発行タスクの準タスク部として実行される。
デバイス登録時のドライバ属性として TDA_DEV_D が指定されたデバイスドライバにおいて、現在実行中の処理開始関数 execfn や完了待ち関数 waitfn から速やかに戻らせたいときに、中止処理関数(64ビット要求パケット) abortfn が呼び出される。この場合、パラメータの要求パケットが64ビットのT_DEVREQ_D* devreq_d
となった点を除き、関数の仕様は32ビット要求パケットの abortfn と同じである。
アプリケーションインタフェースによる通常のデバイス入出力処理とは別の要因によって、デバイスやシステムの状態変化が発生し、それに対してデバイスドライバ側で何らかの処理を要する場合に、ドライバ要求イベントが発生し、イベント関数 eventfn が呼び出される。
ドライバ要求イベントは、電源管理のためのサスペンド/リジューム時(tk_sus_dev 参照)や、USBのような活線挿抜可能な機器の接続時に発生する。
たとえば、tk_sus_dev によってシステムがサスペンドする時には、μT-Kernelの内部で(tk_sus_dev の処理の中で)、サスペンドのドライバ要求イベント(TDV_SUSPEND)を発生し、evttyp
=TDV_SUSPEND として各デバイスのイベント関数を呼び出す。各デバイスのイベント関数の中では、この呼出に応じて、サスペンド時に必要な状態保存などの処理を行う。
ドライバ要求イベントタイプには下記のものがある。
#define TDV_SUSPEND (-1) /* サスペンド */ #define TDV_RESUME (-2) /* リジューム */ #define TDV_CARDEVT 1 /* reserved */ #define TDV_USBEVT 2 /* USBイベント */
上記の値が負のドライバ要求イベントは、サスペンド/リジューム時の処理など、μT-Kernel/SMのデバイス管理内部からの呼出によるものである。
一方、上記の値が正のドライバ要求イベント(TDV_USBEVT)は、tk_evt_dev の呼出によって発生するものであり、μT-Kernelの動作とは直接関係しない参考仕様である。これらのドライバ要求イベントは、USBなどのバスドライバを実装するために、必要に応じて利用する。
イベント関数で行う処理は、イベントタイプごとに定義される。サスペンド/リジュームについては「各デバイスのサスペンド/リジューム処理項」を参照。
tk_evt_dev による呼出の場合、eventfn の戻値はそのまま tk_evt_dev の戻値となる。
イベント関数への要求は、他の要求の処理中であっても受け付け、できる限り速やかに処理しなければならない。
eventfn は、イベント発生の原因となった tk_evt_dev や tk_sus_dev の発行タスクの準タスク部として実行される。
USBイベントで想定している動作は以下のとおりである。
ただし、以下の説明はUSBの機器を扱うデバイスドライバの実装例の説明であり、μT-Kernelの仕様の範囲には含まれない。
USB機器の接続時には、そのUSB機器に対して、実際の入出力処理を行うクラスドライバを動的に対応付ける必要がある。
たとえば、USBメモリなどのストレージを接続した場合には、マスストレージクラス用のデバイスドライバがその機器の入出力処理を行うが、USBカメラを接続した場合には、ビデオクラス用のデバイスドライバがその機器の入出力処理を行う。どのデバイスドライバを使うべきかは、USB機器が接続されるまで分からない。
この時、USB機器とクラスドライバとの対応付けの処理を行うための手段として、USB接続のドライバ要求イベントと、各デバイスドライバのイベント関数を利用する。具体的には、USBポートを監視していたUSBバスドライバ(USBマネージャ)が、新しいUSB機器の接続を検出した場合に、クラスドライバの候補となる各デバイスドライバに対して、USB接続のドライバ要求イベント(TDV_USBEVT)を送り、各デバイスのイベント関数を呼び出す。
各デバイスのイベント関数では、この TDV_USBEVT に応答する形で、新しく接続されたUSB機器への対応の可否を返す。USBバスドライバでは、その戻値を見て、実際のクラスドライバとの対応付けを決定する。
デバイスドライバは、各デバイスで発生した事象を、デバイス事象通知のメッセージとして特定のメッセージバッファ(事象通知用メッセージバッファ)へ送信する。事象通知用メッセージバッファのIDは、各デバイスに対する TDN_EVENT の属性データとして参照あるいは設定される。
デバイス登録の直後には、システムデフォルトの事象通知用メッセージバッファを使用する。デバイスドライバの起動時には、tk_def_dev によってデバイスの登録を行うが、このAPIのリターンパラメータとしてシステムデフォルトの事象通知用メッセージバッファのID値が返されるので、デバイスドライバではその値を保持し、属性データ TDN_EVENT の初期値とする。
なお、システムデフォルトの事象通知用メッセージバッファは、システム起動時に生成され、そのサイズとメッセージ最大長は、システム構成情報の TDEvtMbfSz により定義される。
デバイス事象通知で使用するメッセージの形式は以下のようになる。事象通知のメッセージの内容やサイズは、事象タイプごとに異なる。
typedef struct t_devevt { TDEvtTyp evttyp; /* 事象タイプ */ /* 以下に事象タイプ別の情報が付加される */ } T_DEVEVT;
typedef struct t_devevt_id { TDEvtTyp evttyp; /* 事象タイプ */ ID devid; /* デバイスID */ /* 以下に事象タイプ別の情報が付加される */ } T_DEVEVT_ID;
typedef struct t_devevt_ex { TDEvtTyp evttyp; /* 事象タイプ */ ID devid; /* デバイスID */ UB exdat[16]; /* 拡張情報 */ /* 以下に事象タイプ別の情報が付加される */ } T_DEVEVT_EX;
デバイス事象通知の事象タイプは、以下のように大分類される。
基本事象通知(事象タイプ 0x0001~0x002F)
デバイスからの基本的な事象の通知
システム事象通知(事象タイプ 0x0030~0x007F)
電源制御などシステム全体に関係する事象の通知
拡張情報付き事象通知(事象タイプ 0x0080~0x00FF)
拡張情報を持つデバイスからの事象の通知
ユーザ定義事象通知(事象タイプ 0x0100~0xFFFF)
ユーザが内容を任意に定義可能な事象の通知
事象タイプのうち、代表的なものは以下の通りである。
typedef enum tdevttyp { TDE_unknown = 0, /* 未定義 */ TDE_MOUNT = 0x01, /* メディア挿入 */ TDE_EJECT = 0x02, /* メディア排出 */ TDE_POWEROFF = 0x31, /* 電源スイッチオフ */ TDE_POWERLOW = 0x32, /* 電源残量警告 */ TDE_POWERFAIL = 0x33, /* 電源異常 */ TDE_POWERSUS = 0x34 /* 自動サスペンド */ } TDEvtTyp;
事象通知用メッセージバッファが一杯で事象通知を送信できない場合は、その事象が通知されないことで事象通知の受信側の動作に悪影響が出ないようにしなければならない。メッセージバッファが空くまで待ってから事象通知を行ってもよいが、その場合も原則として事象通知以外のデバイスドライバの処理が滞ってはならない。なお、事象通知の受信側は、できる限りメッセージバッファが溢れることがないように処理しなければならない。
イベント関数(eventfn)へのサスペンド/リジュームイベント(TDV_SUSPEND/TDV_RESUME)の発行により、各デバイスドライバはデバイスのサスペンド/リジューム処理を行う。サスペンド/リジュームイベントは、物理デバイスに対してのみ発行される。
サスペンド処理を開始するためのイベントは次の通りである。
evttyp = TDV_SUSPEND evtinf = NULL (なし)
サスペンドイベント(TDV_SUSPEND)の発行により、次のような手順でサスペンド処理を行う。
現在処理中の要求があれば、完了するまで待つか、中断または中止する。どの方法を選択するかはデバイスドライバの実装に依存する。ただし、できるだけ速やかにサスペンドする必要があるため、完了までに時間がかかる場合は中断または中止としなければならない。
サスペンドイベントは物理デバイスに対してのみ発行されるが、そのデバイスに含まれるすべての論理デバイスに対しても、同様に処理する。
中断:処理を一時的に中断し、リジューム後に続きを行う。 |
中止:中止処理関数(abortfn)による中止と同様に、処理を中止する。リジューム後も再開されない。 |
リジュームイベント以外の新たな要求を受け付けないようにする。
デバイスの電源を切るなどのサスペンド処理を行う。
中止はアプリケーションへの影響が大きいと考えられるため極力避けたい。シリアル回線からの長期の入力待ちなどで、かつ中断とするのが難しい場合以外は中止としない。通常は、終了まで待つか、可能であれば中断とする。
サスペンド期間中にデバイスドライバへ来た要求は、リジュームまで待たせてリジューム後に受け付け処理する。ただし、デバイスへのアクセスを伴わない処理など、サスペンド中でも可能な処理は受け付けてもよい。
リジューム処理を開始するためのイベントは次の通りである。
evttyp = TDV_RESUME evtinf = NULL (なし)
リジュームイベント(TDV_RESUME)の発行により、次のような手順でリジューム処理を行う。
デバイスの電源を入れデバイスの状態を復帰させるなどのリジューム処理を行う。
中断していた処理があれば再開する。
要求受け付けを再開する。