Windows版 iTRONサービスコールの作成 (メッセージバッファ)
(その4)
メッセージバッファへメッセージを送信するサービスコールは以下のとおりです。
サービスコール名 | 説明 |
---|---|
snd_mbf | タイムアウトなしでメッセージバッファへメッセージを送信します。 |
psnd_mbf | ポーリング方式でメッセージバッファへメッセージを送信します。 |
tsnd_mbf | タイムアウト付きでメッセージバッファへメッセージを送信します。 |
メッセージバッファへメッセージを送信する (タイムアウトなし)
タイムアウトなしでメッセージバッファへメッセージを送信するには以下のサービスコールを使用します。
ER snd_mbf( ID mbfid, VP msg, UINT msgsz )
引数 | 説明 |
---|---|
mbfid | メッセージバッファID番号。 |
msg | メッセージバッファに格納するメッセージのポインタ。 |
msgsz | メッセージバッファに格納するメッセージのサイズ。 |
戻り値 | 説明 |
---|---|
E_OK | 正常終了。 |
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
・サービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファへの送信 (タイムアウトなし).
*
* @param [in] mbfid メッセージバッファID番号.
* @param [in] msg メッセージのポインタ.
* @param [in] msgsz メッセージのサイズ.
*
* @retval エラーコード.
*/
ER snd_mbf( ID mbfid, VP msg, INT msgsz )
{
//! タイムアウトなしでメッセージバッファへメッセージを送信する.
return tsnd_mbf( mbfid, msg, msgsz, TMO_FEVR );
}
tsnd_mbf()のタイムアウト設定に TMO_FEVR を指定して呼び出します。
メッセージバッファに空きが無い場合は、メッセージバッファに空きできるまでタスクをスリープさせます。
ポーリングでメッセージバッファへメッセージを送信するには以下のサービスコールを使用します。
ER psnd_mbf( ID mbfid, VP msg, UINT msgsz )
引数 | 説明 |
---|---|
mbfid | メッセージバッファID番号。 |
msg | メッセージバッファに格納するメッセージのポインタ。 |
msgsz | メッセージバッファに格納するメッセージのサイズ。 |
戻り値 | 説明 |
---|---|
E_OK | 正常終了。 |
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_PAR | パラメータエラー。 |
E_TMOUT | メッセージバッファに空きが無い。 |
・サービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファへの送信 (ポーリング).
*
* @param [in] mbfid メッセージバッファID番号.
* @param [in] msg メッセージのポインタ.
* @param [in] msgsz メッセージのサイズ.
*
* @retval エラーコード.
*/
ER psnd_mbf( ID mbfid, VP msg, UINT msgsz )
{
//! 送信待ちなしでメッセージバッファへメッセージを送信する.
return tsnd_mbf( mbfid, msg, msgsz, TMO_POL );
}
tsnd_mbf()のタイムアウト設定に TMO_FEVR を指定して呼び出します。
メッセージバッファに空きが無い場合は、タスクをスリープさせずに戻り値をE_TMOUTとしてすぐにサービスコールから戻ります。
メッセージバッファへメッセージを送信する (タイムアウト付き)
タイムアウト付きでメッセージバッファへメッセージを送信するには以下のサービスコールを使用します。
ER tsnd_mbf( ID mbfid, VP msg, INT msgsz, TMO tmout )
引数 | 説明 |
---|---|
mbfid | メッセージバッファID番号。 |
msg | メッセージバッファに格納するメッセージのポインタ。 |
msgsz | メッセージバッファに格納するメッセージのサイズ。 |
tmout |
タイムアウトの設定値。
|
戻り値 | 説明 |
---|---|
E_OK | 正常終了。 |
E_ID | 範囲外のメッセージバッファID番号。 |
E_NOEXS | 指定したメッセージバッファID番号は登録されていない。 |
E_TMOUT | メッセージバッファに空きが無い。 |
E_PAR | パラメータエラー。 |
E_CTX | 非コンテキスト・タスクからの呼び出し。 |
・サービスコールのソースコードは以下のようになります。
/****************************************************************************/
/*!
* @brief メッセージバッファへの送信 (タイムアウトあり).
*
* @param [in] mbfid メッセージバッファID番号.
* @param [in] msg メッセージのポインタ.
* @param [in] msgsz メッセージのサイズ.
* @param [in] tmout タイムアウト設定.
*
* @retval エラーコード.
*/
ER tsnd_mbf( ID mbfid, VP msg, UINT msgsz, TMO tmout )
{
ER ercd;
wi_CommonLock();
//! メッセージバッファへメッセージを送信する.
ercd = wi_SendMsgBuffer( mbfid, msg, msgsz, tmout );
wi_CommonUnlock();
return ercd;
}
指定したメッセージバッファID番号のメッセージバッファへメッセージを格納します。
メッセージバッファに空きが無い場合は、メッセージバッファに空きができるか、タイムアウトで設定した時間が経過するまでタスクをスリープさせます。
メッセージバッファへメッセージを格納する関数
メッセージバッファへメッセージを格納する関数のソースコードは以下のとおりです。
/****************************************************************************/
/*!
* @brief メッセージ・バッファへの送信.
*
* @param [in] id メッセージバッファID番号.
* @param [in] msg メッセージのポインタ.
* @param [in] size メッセージのサイズ.
* @param [in] tmout タイムアウト設定.
*
* @retval エラーコード.
*/
ER wi_SendMsgBuffer( INT id, VP msg, INT size, TMO tmout )
{
ER ercd;
WIMBFOBJ *p;
WITSKOBJ *tsk1,*tsk2;
//! メッセージバッファIDのオブジェクトを取得する.
p = (WIMBFOBJ *)wi_FindObject( id, TMAX_MAXMBF, ObjList, &ercd );
if( !p ){
return ercd;
}
//! 引数が不正な場合はエラーにする.
if( !msg || !size ){
return E_PAR;
}
//! メッセージ待ちをしているタスクがある場合.
if( p->RcvWaitQue ){
//! メッセージ待ちをしているタスクにメッセージを渡す.
tsk1 = (WITSKOBJ *)(p->RcvWaitQue);
if( tsk1->Param[0] ){
memcpy( tsk1->Param[0], msg, size );
tsk1->Param[1] = (VP)size;
}
//! メッセージ待ちをしているタスクを起床する.
wi_TaskWakeup( tsk1->Hdr.Id, TTW_RMBF, &(p->RcvWaitQue) );
return E_OK;
}
//! 自タスクのタスクコンテキストを取り出す.
tsk1 = wi_GetTaskObject( TSK_SELF );
//! 非タスクコンテキストからの呼び出しの場合はエラーにする.
if( !tsk1 ){
return E_CTX;
}
//! 送信待ちのタスクがある場合.
if( p->SndWaitQue ){
/*!
* 待ち行列がタスク優先度順の場合、待ち行列の先頭のタスクの優先度より
* 今回のタスク(自タスク)の優先度が大きいければ、メッセージバッファに
* 待ち行列のタスクよりも先に送信メッセージを格納する.
*/
if( p->SndAttr & TA_TPRI ){
tsk2 = (WITSKOBJ *)(p->SndWaitQue);
if( tsk1->TaskPri > tsk2->TaskPri ){
if( PutMesageData( p, msg, size ) ){
return E_OK;
}
}
}
}else{
//! メッセージバッファの最後に送信メッセージを格納する.
if( PutMesageData( p, msg, size ) ){
return E_OK;
}
}
//! タスクコンテキストに送信メッセージを格納しておく.
tsk1->Param[0] = msg;
tsk1->Param[1] = (VP)size;
//! バッファが空くまでタスクをスリープさせる.
return wi_TaskWait( id, TTW_SMBF, tmout, p->SndAttr, &(p->SndWaitQue) );
}
メッセージバッファへメッセージを格納する関数は以下のような処理を行います。
- 引数で指定されたメッセージバッファID番号に該当するメッセージバッファ・オブジェクトを取り出します。
- メッセージ受信待ちをしているタスクがあれば、送信メッセージを受け渡して正常終了します。このとき受信待ちをしているタスクを起床します。
- 他にメッセージ送信待ちのタスクがある場合、メッセージ待ち行列の先頭のタスクよりもタスク優先度が高いときは、他のタスクよりも先にメッセージを格納します。
先頭のタスクよりタスク優先度が低い場合、バッファが空くか、タイムアウトになるまでタスクをスリープさせます。 - メッセージ待ちをしているタスクがない場合、メッセージバッファの最後にメッセージを追加します。
メッセージバッファにメッセージを追加できなかった場合、バッファが空くか、タイムアウトになるまでタスクをスリープさせます。
/****************************************************************************/
/*!
* @brief メッセージバッファにメッセージを格納する.
*
* @param [in] p メッセージバッファ・オブジェクトのポインタ.
* @param [in] msg メッセージを格納した領域のポインタ.
* @param [in] len メッセージのサイズ.
*
* @retval TRUE = OK. / FALSE = NG.
*/
static BOOL PutMesageData( WIMBFOBJ *p, BYTE* msg, INT len )
{
INT i;
INT free_size;
//! メッセージバッファに空きがない場合は処理を終了する.
free_size = GetFreeSize( p );
if( free_size < len ){
return FALSE;
}
/*!
* メッセージ・サイズの途中でリングバッファを折り返えすかもしれないので
* メッセージ・サイズを1バイトづつバッファに格納する.
*/
for( i = 0; i < 4; i++ ){
static int shift[4] = { 24, 16, 8, 0 };
//! メッセージのサイズを1バイトづつ格納する.
p->MsgBuf[p->PutPos] = (BYTE)((((DWORD)len) >> shift[i]) & 0xFF);
//! バッファの書き込み位置を更新する.
p->PutPos = wi_IncRingPositon( p->PutPos, p->MsgSize );
}
//! メッセージがリングバッファを折り返す場合は2回に分けてメッセージを格納する.
if( (p->PutPos + len) > p->MsgSize ){
DWORD size1,size2;
//! リングバッファの最後にメッセージの先頭データをコピーする.
size1 = p->MsgSize - p->PutPos;
memcpy( &p->MsgBuf[p->PutPos], msg, size1 );
//! リングバッファの先頭に残りのメッセージをコピーする.
size2 = len - size1;
memcpy( p->MsgBuf, &msg[size1], size2 );
//! バッファの書き込み位置を更新する.
p->PutPos = size2;
}else{
//! バッファにメッセージをコピーする.
memcpy( &p->MsgBuf[p->PutPos], msg, len );
//! バッファの書き込み位置を更新する.
p->PutPos += len;
}
return (INT)len;
}
メッセージバッファには、「メッセージのバイト数」+「メッセージデータ」の形式に送信メッセージを格納します。
メッセージバッファはリングバッファにしていあるので、「メッセージのバイト数」をバッファの領域から、はみ出さないように1バイトづつ書き込みます。
「メッセージデータ」の方は、1バイトづつだと効率が悪いので、データの書き出し位置からバッファの先頭にまたがるかどうかを判定して、2回に分けて書き込みます。