ファイルシステムアプリケーションの解説

つぶつぶコラム

はじめに

今回はFATファイルシステム「GR-FILE」を使って、簡単なファイルシステムアプリケーション作成の流れを書いていこうと思います。GR-FILE固有のAPIを使用しますが、処理の流れは他のファイルシステムでも使えると思います。

初期化

GR-FILEを使用するには、まず最初に、様々なパラメータ設定をし、またGR-FILEが管理する情報の為のメモリブロックを割り当てる必要があります。
パラメータに関しては初期設定でも動作するように設定されていますが、メモリを節約したい、パフォーマンスを検討したい、などがある場合にはパラメータ値を検討する必要があります。パラメータのキャッシュバッファに関する設定は、GR-FILEに割り当てるメモリブロックのサイズにも影響しますので、変更する場合はこの点にも注意が必要です。
パラメータに関しては、納品時に提供される各種ドキュメント(GR-FILE取扱説明書.pdf、GR-FILEポーティングマニュアル.pdf、メモリプール概算.xls)を参照してください。
メモリブロックを割り当てた後、GR-FILEの初期化関数を呼び出すことで、以降GR-FILEのAPIが使用可能になります。メモリブロック、初期化関数は以下の様に記述します。(GR-FILEのサンプルアプリケーションから抜粋。)

    /*— GR-FILE向けメモリ管理の初期化 —*/
    /* メモリエリアの取得 */
    pcPoolArea = _usb_test_GetMemoryArea();
    if (pcPoolArea == 0) {
        /* error */
        return USB_TEST_NG;
    }
    /* メモリ管理の初期化 */
    grp_mem_vl_init( pcPoolArea, FILE_POOL_SIZE);
 
    /*— GR-FILEの初期化 —*/
    /* configurationパラメータ(必要ならば) */
    /* 本アプリケーションではデフォルト値を利用する */
    /* GR-FILEの初期化 */
    if (grp_fs_init() != 0) {
        /* error */
        return USB_TEST_NG;
    }
メモリ割り当てと初期化

初期化でエラーになる場合は、割り当てられたメモリブロックのサイズや、OS資源(セマフォ)の数、GR-FILEのパラメータ値を確認してください。

マウント/アンマウント

接続したメディア内のファイルシステム情報を読み取り、使用できる状態にする、あるいは使用を終了する処理を行います。また、アンマウントではGR-FILEのキャッシュバッファの書き戻しも行います。
マウントでは様々な機能をAPIの引数で指定することが可能で、読み込み専用としたり、GR-FILEのキャッシュバッファの反映方法を指定することが出来ます。
ファイルの読み書きなどのAPIは基本的にはマウント~アンマウントの間で使用できます。詳細は、GR-FILE取扱説明書.pdfを参照ください。
マウント/アンマウントは以下の様に記述します。(GR-FILEのサンプルアプリケーションから抜粋。)

    iRet = grp_fs_mount(aucDevName, “/”, “fat”, GRP_FS_SYNC_FL_CLOSE);
    if (iRet == 0) {
        GRDBG_PRINT(”     O mount ok\r\n”);
    }
マウント
     iRet = grp_fs_unmount(aucDevName, 0);
    if (iRet == 0) {
        GRDBG_PRINT(”     O unmount ok\r\n”);
    }
アンマウント

ファイル名の列挙

GR-FILEにはディレクトリ内のファイル情報(ディレクトリエントリと呼びます)を1つ取得するAPI(grp_fs_get_dirent)があります。grp_fs_get_dirent() は引数にファイルハンドルと、取得した情報を格納するポインタを指定します。
ファイルハンドルには、オープンしたディレクトリのファイルハンドルを指定します。-1を指定するとカレントディレクトリにあるディレクトリエントリを取得することが出来ます。
また、指定するポインタに検索開始位置を指定することで、取得するディレクトリエントリの検索開始位置を変えることが出来ます。(下記ソースのtDirent.uiStartを更新しているのはこのため。)
このAPIを、検索開始位置を変えながら複数回使用することで、ファイル名の列挙を行うことが出来ます。
また、grp_fs_get_dirent() はロング名とショート名の情報を別々に取得しますので注意が必要です。grp_fs_get_dirent() で取得できる情報についてはGR-FILE取扱説明書.pdfを参照ください。
ファイル名の列挙は以下の様に記述します。(GR-FILEのサンプルアプリケーションから抜粋。)

    tDirent.pucName     = aucName;                          /* set file aucName buffer      */
    tDirent.sNameSize   = _USB_TEST_FILE_NAME_MAX*2;        /* set aucName buffer size      */
    tDirent.uiStart     = 0;                                /* start offset is 0            */
    tDirent.uiEnd       = 0;                                /* end offset is 0              */
    while ((iRet = grp_fs_get_dirent(iHdr, &tDirent)) > 0) {
        GRDBG_PRINT(“%s\r\n”, tDirent.pucName);
        tDirent.sNameSize = _USB_TEST_FILE_NAME_MAX*2;      /* set aucName buffer size      */
        tDirent.uiStart   = tDirent.uiEnd;                  /* set next                     */
    }
ファイル名の列挙

サンプルでは、iHdrに-1を指定していますので、カレントディレクトリのファイル名を列挙しますが、iHdrにディレクトリをオープンした際のファイルハンドルを指定することで、オープンしたディレクトリのファイル名を列挙できます。開いたディレクトリのファイルハンドルは、使い終わったらクローズしないとオープンされたままになるので注意が必要です。

ファイル処理の一連の流れ

ファイルに関する処理をアプリケーションに実装する場合、ある程度の決まった流れがあります。
基本的な流れは、ファイルオープン(クリエイト)、ファイルリード、ファイルライト、ファイルクローズ、です。ファイルに関する処理を行う前にマウントを行っておくことも必要になります。
マウント/アンマウントも含めた大まか流れとしては以下のようになります。

大まかな流れ

上記では対になる処理を同じ色にしています。
つまり、マウントを行ったら、アンマウントを行う必要がある、と言う意味です。例外としてファイルリード、ファイルライトは対では無いのでご注意ください。(ファイルリードだけを行う事も可能ですので。)
また、多くの場合、ファイルオープン~ファイルクローズまでは同一のタスクで行う事が多いです。

ファイルオープン

ファイルをオープンしてファイルハンドルを得ます。ファイルハンドルはファイルの使用が終わった時に、ファイルクローズで指定します。
ファイルハンドルは有限の資源なので、枯渇すると新たにファイルをオープンすることが出来なくなりますので使い終わったら忘れずにファイルクローズします。
ファイルハンドルの数はGR-FILEのパラメータで設定できます。
ファイルオープンで得たファイルハンドルは、ファイルリード、ファイルライト、ファイルクローズなどのAPIで指定します。
ファイルオープンの際にはファイルへのパス、動作モードと保護モードを指定できます。動作モードには読み込みのみできるモードにするか、書き込みのみできるモードとするか、両方出来るモードでオープンするかなどを指定します。保護モードも設定は可能ですが、FATでは読み込み専用以外の保護モードは意味がありません。引数などの詳細についてはGR-FILE取扱説明書.pdfを参照ください。
ファイルオープンは以下の様に記述します。(GR-FILEのサンプルアプリケーションから抜粋。)

    ret = grp_fs_open(av[1], mode, prot);
ファイルオープン

ファイルオープンが成功すると、戻り値としてファイルハンドルが返されます。以降は返されたファイルハンドルを使用してファイルアクセスを行います。
grp_fs_open() はファイルのオープンだけでなく、ディレクトリのオープンにも使用します。指定されたパスがディレクトリの場合はディレクトリがオープンされ、ファイルハンドルが返されます。ファイルと異なるのはディレクトリの作成は出来ない点です。ディレクトリを作成する場合はgrp_fs_create() を使用します。ディレクトリを開いた際のファイルハンドルはgrp_fs_get_dirent() に使用できます。詳細についてはGR-FILE取扱説明書.pdfを参照ください。

ファイルクローズ

ファイルオープンで開いたファイルを閉じます。
開いたファイルハンドルは閉じないとファイルハンドルの資源が解放されません。ファイルオープンがエラーになった場合はファイルクローズを行う必要はありません。(ファイルハンドルが返されていない為ファイルクローズを行えません。)
ディレクトリを開いた場合も、ファイルハンドルを指定してファイルクローズを行います。ファイルクローズの詳細についてはGR-FILE取扱説明書.pdfを参照ください。
ファイルクローズは以下の様に記述します。(GR-FILEのサンプルアプリケーションから抜粋。)

    ret = grp_fs_close(fd);
ファイルクローズ

ファイルリード

ファイルから指定サイズ分のデータを読み取ります。

ファイルハンドルには、ファイルオープンで返された値を指定しますが、他のタスクでファイルオープンしている値を指定してもエラーになります。(GR-FILEのコンパイルスイッチで変更できます。)

バッファアドレスには読み込んだデータを格納する領域のアドレスを指定しますが、ファイルオープン時にDirectIOを指定していた場合は注意する必要があります。と言うのも、DirectIOを指定した場合は、ファイルリードで引数に指定したアドレスがそのまま下位ドライバに渡されます。そのため、下位ドライバがサポートできないアドレス(例えば奇数アドレスなど)を指定するとエラーになったり、ハングアップの可能性があります。DirectIOを指定しない場合は、一度GR-FILE内のキャッシュバッファに読み込まれ、キャッシュバッファからアプリケーションバッファへコピーされます。このコピーはCPUで行うのでアドレスの問題は発生しません。(その代わり、コピーの分パフォーマンスが低下します。)

ファイルリードの詳細についてはGR-FILE取扱説明書.pdf、アプリケーションノートを参照ください。
ファイルリードは以下の様に記述します。(GR-FILEのサンプルアプリケーションから抜粋。)

        ret = grp_fs_read(fd, buf, iread);
ファイルリード

ファイルライト

ファイルへ指定サイズ分のデータを書き込みます。

ファイルハンドルには、ファイルオープンで返された値を指定しますが、他のタスクでファイルオープンしている値を指定してもエラーになります。(GR-FILEのコンパイルスイッチで変更できます。)

バッファアドレスには書き込むデータが格納された領域のアドレスを指定しますが、ファイルオープン時にDirectIOを指定していた場合は注意する必要があります。と言うのも、DirectIOを指定した場合は、ファイルライトで引数に指定したアドレスがそのまま下位ドライバに渡されます。そのため、下位ドライバがサポートできないアドレス(例えば奇数アドレスなど)を指定するとエラーになったり、ハングアップの可能性があります。DirectIOを指定しない場合は、一度GR-FILE内のキャッシュバッファにコピーされ、キャッシュバッファからメディアへ書き込みが行われます。アプリケーションバッファからキャッシュバッファへのコピーはCPUで行うのでアドレスの問題は発生しません。(その代わり、コピーの分パフォーマンスが低下します。)

ファイルライトの詳細についてはGR-FILE取扱説明書.pdf、アプリケーションノートを参照ください。
ファイルライトは以下の様に記述します。(GR-FILEのサンプルアプリケーションから抜粋。)

        ret = grp_fs_write(fd, buf, iwrite);
ファイルライト

まとめ

ファイルの読み書きを行う、簡単なプログラムであれば、ここまでのAPIを使用することで作ることが出来ますが、もう少し複雑な処理となると他のAPIを使う必要が出てきます。(SEEKなど。)
GR-FILEには今回使ったAPI以外にも多くのAPIがあり、様々な処理に対応できるミドルウェアとなっています。
もし、「こんな場合どうしたら良い?」、「こんな処理を作るにはどうしたら良い?」などあれば、是非お知らせください。今後のブログ記事にて触れていきたいと思います。