[Linuxの基本] -V4L2フレームワーク-メディアデバイス

この記事では、V4L2のランタイムデータストリームデバイス管理について詳しく説明します。これには、[ランタイムデバイス管理]と呼ばれるもの、その用途、使用方法などが含まれます。この記事の目的は、meidaデバイスのコーディングの使用法と機能アプリケーションを習得することです。

一、メディアフレームワーク

1.1はじめに

関連するコントロールAPIはDocumentation / DocBook / media / v4l / media-controller.xmlにあります。このドキュメントは、コアテストメディアフレームワークの実装に焦点を当てています。注:直接表示しても何も表示されません。カーネルのルートディレクトリにあるhtmldocsなどを作成してください。表示は簡単です。

1.2。実行時の制御機器

つまり、デバイスの起動後のデータフローライン制御は、工場の組立ラインと同じように、組立ラインの各ノード(ラベル付け、印刷、パッケージング)は、入力デバイスの各サブデバイスと同じです。実行時のデバイス制御は次のとおりです。たとえば、ノードを制御できるという効果を実現するには、ラベル付け用のマシンがいくつかあります。このパイプライン処理では、シルクスクリーンを追加するかどうか、追加するマシンを選択する必要があります。等々。

1.3、役割

リアルタイムのパイプライン管理を提供します。パイプラインはパイプラインとして理解されます。水道管を想像してください。内部の水はデータストリームです。入力デバイスのcsi-> isp-> videoはパイプラインラインを形成します。メディアフレームワークは、パイプラインのオープン、クローズ、エフェクト制御、ノード制御などの機能を提供します。

1.4使用方法

カーネルは主に、media_device、media_entity、media_link、media_padの4つの構造を使用して多数のノードを編成します。メディアフレームワーク全体は、これら4つの構造の周りで使用されます。これについては、以下で詳しく説明します。

1.5、抽象的なデバイスモデル

メディアフレームワークの目的の1つは、デバイストポロジを検出し、実行時に構成することです。この目標を達成するために、メディアフレームワークはハードウェアデバイスをエンティティに抽象化し、エンティティはリンクを介して接続されます。

1.エンティティ:ハードウェアデバイスモジュールの抽象化(回路基板上のさまざまなコンポーネントとチップに類似)

2、パッド:ハードウェアデバイスポートの抽象化(アナログコンポーネント、チップ上のピン)

3.リンク:ハードウェアデバイスの接続の抽象化。リンクの両端はパッド(アナログコンポーネントのピン間の接続)です。

#------------#                #------------#
|          __|__            __|__          |
|         |  |  |   link   |  |  |         |
|         | pad |<-------->| pad |         |
|         |__|__|          |__|__|         |
|            |                |            |
|   entity   |                |   entity   |
#------------#                #------------# 

エンティティ間の接続を確立する必要がある場合、リンクとエンティティ情報をパッドに格納する必要があると想像してください。リンクはパッドとエンティティ情報を格納する必要があり、エンティティはリンクとパッド情報を格納する必要があります。あなたと私。あなたの場合。

2、メディア機器

メディアデバイスはmedia_device構造体で表されます。通常、構造体はより大きなデバイス定義構造体に埋め込まれている必要があり、ほとんどの場合、media_deviceとv4l2_deviceは並列レベルにあります。または、omap3ispのコードは次の例です。

struct isp_device {
    struct v4l2_device v4l2_dev;
    struct v4l2_async_notifier notifier;
    struct media_device media_dev;
    struct device *dev;
    u32 revision;
    ... ...
}

2.1。次の機能を使用して、メディアデバイスを登録します。

media_device_register(struct media_device * mdev);

関数の呼び出し元は、登録前に次の構造体メンバーを設定する必要があります(事前に構造体を初期化するのは呼び出し元の責任です)。

  • dev:親デバイス、通常はプラットフォームデバイスのデバイスメンバーを指す必要があります。
  • モデル:モデルの名前。

次のメンバーはオプションです。

  • シリアル:シリアル番号、一意である必要があります
  • bus_info:バス情報。PCIデバイスの場合は「PCI:」に設定できます。
  • hw_revision:ハードウェアバージョン。可能であれば、KERNEL_VERSIONマクロ定義でフォーマットする必要があります
  • driver_version:ドライバーのバージョン。最終的なデバイスノードの名前はmedia [0-9]であり、ノード番号はカーネルによって自動的に生成されます。

2.2。次の機能を使用して、デバイスをアンインストールします

media_device_unregister(struct media_device * mdev);

登録されていないデバイスをアンインストールすることは***安全ではない***ことに注意する必要があります。個人の表示コードの推測が安全でない主な理由はいくつかあります。

  1. 登録されていない場合、media_device内のエンティティメンバーが初期化されない可能性があります。その値が不確定な値である場合、不正アクセスが発生します。
  2. 登録されていない場合、内部devnodeメンバーは初期化されず、アンインストール中に問題が発生します。

三、エンティティ、パッド、リンク

3.1、エンティティ

エンティティはmedia_entity構造で表されます。これは通常、v4l2_subdevやvideo_device構造などのより大きな構造に埋め込まれています(自分でスペースを割り当てる必要はありません。構造はすでに含まれています)。もちろん、直接割り当てることもできます。エンティティ。次の関数を使用して、エンティティを初期化します。

media_entity_init(struct media_entity * entity、u16 num_pads、struct media_pad * pads、u16 extra_links);

初期化関数を実行する前に注意する必要のあるパラメーターは次のとおりです。

  • num_pads:ドライバーサブデバイスの構造に関連するパッドの数。
  • pads:media_pad構造体配列。通常、padはドライバー定義の構造体に埋め込まれ、配列アドレスがこのパラメーターに渡されます。パッドは事前に初期化する必要があります。
  • extra_links:この関数は、num_padsに基づいてリンクの数を割り当てます。このパラメーターは、事前に割り当てられた数に加えて、必要な追加のリンクの数を示します。
  • エンティティ:media_entityの名前、タイプ、フラグ、リビジョン、およびgroup_idは、初期化の前または後に設定する必要があります。構造が高レベルの構造に埋め込まれている場合、これらのメンバーは高レベルのフレームワークコードによっても設定できます。 idは登録時に入力されます(idメンバーが事前に設定されている場合、プリセット値は登録時に維持されます)。エンティティには、そのステータスと機能を識別するための関連フラグ[フラグ]があります。MEDIA_ENT_FL_DEFAULTは、これがデフォルトのエンティティであることを意味します。複数のエンティティのグループIDを同じ整数に設定して、それらが同じカテゴリに属していることを識別することができます。カーネルの場合、グループIDは役に立ちませんが、エンティティが列挙されるときにグループIDがユーザースペースに渡されます。ユーザースペースの特定の状況で役立ちます。

3.2、パッド

パッドはmedia_pad構造体で表され、パッドデータはドライバー(配列形式)によって管理されます。パッドは一意の識別にエンティティと配列の添え字を使用します。エンティティ内のIDは繰り返されませんが、異なるエンティティ間のパッドIDが繰り返される可能性があるため、パッドのインデックスはエンティティとIDによって共同で確認される必要があります。

パッドの数は事前にわかっているため(作成するチップ、ピンの数を知っている必要があります)、media_pad構造は動的に割り当てられなくなり、ドライバーは構造配列の管理を担当する必要があります(動的割り当ては避けてください)。 。ドライバーは、media_entity_init関数が呼び出される前に、パッドの方向属性を設定する必要があります。パッドには、その属性を示すフラグビットがあります。初期化中にこのメンバーを設定するだけで、残りはmedia_entity_init関数によって行われます。

MEDIA_PAD_FL_SINK:    目的 pad
MEDIA_PAD_FL_SOURCE:  源 pad

3.3、リンク

リンクはmedia_link構造で表されます。各エンティティのすべてのパッドは、それに関連するすべてのリンクを格納します。リンクは、正と負の両方向のトラバーサルを実現するために、それぞれソースパッドと宛先パッドによって格納されます。 。次の関数を使用してリンクを作成します。

media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags);

リンクには、その属性を識別するためのフラグビットがいくつかあります。

MEDIA_LNK__FLENABLED:link 被使能,可以用来传输数据,多个 link 连接到同一个 sink pad 时,只有一个 link 可能被使能。
MEDIA_LNK_FL_IMMUTABLE:link 的使能状态不能在运行时被改变,一般情况下这两个标志位同时被设置。
MEDIA_LNK_FL_DYNAMIC:link 的状态是动态可变的。

パッドとは異なり、リンクの数は必ずしも事前に確認されているわけではありません(回路基板では、ピンに接続する必要のあるデバイスの数を完全に確認できない場合があり、一時的に変更される可能性が非常に高くなります)。したがって、media_entity_init関数着信パラメータに基づいて、一定量のmedia_link構造が事前に割り当てられます。十分でない場合は、media_entity_create_linkに動的に割り当てられます(リンクの数がmax_link以上の場合、リンクの数は拡張されます) )。

4、登録とアンインストール

ドライバーは、エンティティを登録およびアンインストールするために次の関数を使用する必要があります(手動で実行する必要はありません。v4l2_device_un/ register_subdev関数で実行されます)。

media_device_register_entity(struct media_device *mdev, struct media_entity *entity);
media_device_unregister_entity(struct media_entity *entity);

カーネルは一意の正の整数を使用して各エンティティを表し(同じmedia_deviceの下で一意)、ドライバーはmedia_entity-> idメンバーを入力してエンティティのID値を指定することもできますが、一意である必要があります。IDがカーネルによって自動的に生成される場合、それらが継続的であるという保証はありません。実際、カーネルによって自動的に生成されるIDは、エンティティのmedia_device-> entity_id ++によって割り当てられ、値はで1に初期化されます。 media_device_register関数Liman。

エンティティをアンロードした後、次の関数を呼び出して、適用された関連リソースを解放する必要があります。主に、動的に割り当てられたmedia_link構造メモリを解放します。

media_entity_cleanup(struct meida_entity *entity); //与 media_entity_init 结对使用

エンティティをトラバースするには、ユーザースペースでMEDIA_IOC_ENUM_ENTITIESシステムコールを実行できます。media_entity_descのIDを(0 | MEDIA_ENT_ID_FLAG_NEXT)に設定する必要があります。ループ中は、ID | = MEDIA_ENT_ID_FLAG_NEXTを設定するだけでエンティティトラバースプロセスを完了できます。指定されたエンティティを列挙する必要がある場合は、idを指定されたエンティティのid値に設定する必要があります(カーネルエンティティのIDは1から始まります)。これは、エンティティ登録機能で確認できます。コード例は次のとおりです。投稿されたコードを合理化して、スペースを占有しすぎないようにしました。

int enum_media_device_entities(int iFd)
{
    int iRet;
    struct media_entity_desc desc;
    
    desc.id = 0 | MEDIA_ENT_ID_FLAG_NEXT;
    while(1) {
        iRet = ioctl(iFd, MEDIA_IOC_ENUM_ENTITIES, &desc);
        if(iRet < 0) {
            MODULE_WRN("enum media entities end \r\n");
            break;
        }
        MODULE_DBG("entity name [%s]\r\n", desc.name);
        desc.id |= MEDIA_ENT_ID_FLAG_NEXT;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    int iErr = 0, ivFd;
    ivFd = open("/dev/media0", O_RDWR);
    iErr = enum_media_device_entities(ivFd);
    
    close(ivFd);
  open_err:
    return iErr;
}

5.グラフ走査(深さ優先)

グラフ走査は何をしますか?実行時に指定された各エンティティにアクセスする方法を提供します。アクセスが必要な理由は、実行時にそれらを管理する必要がある場合があるためです。

次の関数を使用して、同じメディアデバイスに属するエンティティをトラバースできます(線形トラバーサル、非定型グラフトラバーサル、つまり、リンクリストと同じトラバーサル方法)。

struct media_entity *entity;
media_device_for_ench_entity(entity, mdev) {
    /* entity will point to each entity in turn */
    ... ...
}

ドライバーは、有効なリンクを介して、特定のエンティティからアクセス可能なすべてのエンティティをトラバースする必要がある場合があります。メディアフレームワークは、このタスクを実行するために深さ優先APIを提供します。閉ループグラフをトラバースしないようにする必要があることに注意してください。トラバースしないと、無限ループに陥ります。この状況を回避するために、関数は最大トラバース深度をMEDIA_ENTITY_ENUM_MAX_DEPTHに制限します。このマクロの最新の定義は次のとおりです。 16 ** [Linux以降-4.4。138] **。

media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity);
meida_entity_graph_walk_next(struct media_entity_graph *graph);

を使用する場合は、最初に最初の関数でグラフを初期化し、次に2番目の関数を呼び出してループ内をトラバースします。トラバーサルが完了すると、2番目の関数はNULLを返します。トラバーサルプロセスはいつでも中断でき、クリーンアップ関数を呼び出す必要はありません。

2つの特定のパッドのリンクを検索したり、1つのパッドを介してそれに接続された別のパッドを検索したりするための対応するヘルパー関数があります。

media_entity_find_link(struct media_pad *source, struct media_pad *sink);
media_entity_remote_pad(struct media_pad *pad);

6、リンクの設定を補足する

リンクの属性は実行時に変更できます。次の関数を呼び出すだけで完了します。

media_entity_setup_link(struct media_link *link, u32 flags);

flagsパラメータは、指定されたリンクの属性を設定するために使用されます。許可される属性は、MEDIA_LINK_FL_ENABLED属性からMEDIA_LINK_FL_ENABLEまたはMEDIA_LINK_FL_DISABLEフラグまでです。リンクがMEDIA_LINK_FL_IMMUTABLEフラグで設定されている場合、有効または無効にすることはできません。リンクが有効または閉じられると、メディアフレームワークはソース側のシンクとlink_setupを呼び出して、関連する設定を2回呼び出します。2番目の呼び出しが失敗した場合、最初の呼び出しも復元されます。

メディアデバイスドライバーは、link_setup操作の完了後に呼び出されるコールバック関数を指すようにmedia_device-> link_notifyを設定できます。リンクが不変でない場合、エンティティドライバはそれ自体でlink_setup操作を実装する必要があります。リンクの構成が他のリンクに影響を与えることはありません。リンクがシンクパッドに接続されていて、リンクが有効になっている場合、パッドへの他のリンクは有効にできなくなり、この時点で-EBUSYが返されます。

6.1 pipeline 与 media 流

パイプラインの概念は以前に導入されたものであり、繰り返されません。これは説明図です(実際、これはあまり明確で理解しにくいものであり、何らかの理由でリリースできないものがより明確で理解しやすいものです。 。図によると、想像力を働かせてください。上記の説明は、1つのポイントにのみ焦点を当てている限り、それ自体で画像を抽象化します。パイプラインはデータフローリンクの抽象化です。

ストリーミングがオンになっている場合、ドライバーはパイプライン上のすべてのエンティティに通知して現在の状態を変更せずに維持する必要があります。次の関数を呼び出して通知を完了することができます(この関数はシンク側のvalidateコールバックのみを呼び出します)。

media_entity_pipeline_start(struct media_entity *entity, struct media_pipeline *pipe);

この関数は、直接または間接を問わず、イネーブルリンク接続上のすべてのエンティティをストリーミング状態としてマークします。2番目のパラメーターが指すmedia_pipeline構造体は、パイプライン上のすべてのエンティティーに渡されます。ドライバーは、media_pipeline構造体を上位レベルの構造体に埋め込む必要があり、media_entity構造体からパイプラインにアクセスできます。ストリーミングを停止する必要がある場合は、次の電話番号に電話する必要があります。

media_entity_pipeline_stop(struct media_entity *entity);

start関数は、それに対応してネストされた方法で呼び出すことができるため、stop関数も対応する呼び出し回数を保持する必要があります。media_entity_pipeline_start関数は、リンクの有効性をチェックします。このとき、media_entityのlink_validateメンバーを使用してチェックを完了します。次の図は、トラバーサルの図です。

                                                                                                     パイプライントラバーサル

上の図の丸番号は、訪問の順序を示しています。幅優先グラフ走査のカーネルの実装は非常に興味深く、参照値であり、カーネルコードを自分で見つける価値があることは言及する価値があります。その中で、スタックやビットマップなどの概念が使用されます。特定のコードは、media-entity.c / media_entity_pipelien_start関数にあります。

上記のトラバーサルは幅優先ですべてのトラバーサルであるため、つまり、ISPに2つの入力ソースがある場合、これら2つの入力ソースが同時にオンになる可能性があります。多くの場合、これを発生させたくありません。指定された入力ソースと指定されたシングルリンクパイプラインが開かれていることが期待されます。この時点で、パイプラインを自分で管理する必要があり、関連する管理のために独自のパイプライン構造を実装できます。実装は非常に単純で、このパイプライン構造は次のようになります。

struct spec_pipeline {
    struct list_head entities;    //pipeline 线上的 entity 链表头
    struct media_entity entities[PIPE_LENGTH];     //与上一个类似,随自己想法去实现
    int (*open_fun)(struct media_entity entity, int onoff);
    int (*s_stream)(struct media_entity entity, int onoff);
    ... ...
};

チップ:

1. media_entityからv4l2_subdevを見つけるには、media_entity_to_v4l2_subdev関数を使用します。

2.ビデオモジュールのstreamon部分にリンクの有効化フラグを追加できます。media_entity_pipeline_start関数はentity.stream_countメンバーに値を追加し、2番目のパラメーターのパイプをリンクラインのentity.pipeメンバーに渡します。有効化フラグが完了した後、各エンティティでグラフ走査を実行して、そのset_streamメンバーを呼び出してストリームを開始できます。実際、s_param、s_fmt、およびその他のioctlが呼び出されると、関連する設定のためにパイプライン全体の各エンティティのコールバック関数を呼び出すためにグラフ走査が必要になります。

3.すべてのサブデバイスノードが登録されたら、media_entity_create_linkを介して各エンティティの接続を完了し、後でデータストリーム全体が開かれるのを待ちます。

4.なぜメディアフレームワークがあるのですか?subdevしかないため、各サブデバイスクラスはレベル状態にあり、データフローの方向に違いはありません。データフローパイプラインを確立する必要がある場合は、自分で実装する必要があります。メディアフレームワークを使用すると、これらの管理ははるかに便利になります。パイプラインのすべての管理操作を提供し、多くのサブ開発者を完全なデータストリームとして直列に接続して、正しいデータストリームを実行できるからです。指示されたデータ転送。

5. v4l2_subdevとvideo_deviceの両方にmedia_entity構造(非ポインタータイプ)があります。video_devcieのエンティティはビデオデバイスの登録に登録され、名前はvideo_device名になります。2つは異なります。使用する場合は特別な必要があります注意。

おすすめ

転載: blog.csdn.net/u014674293/article/details/111488115