【V4L2】V4L2サブデバイス

シリーズ記事の目次

[V4L2] V4L2 フレームワークの簡単な説明
[V4L2] V4L2 フレームワークのドライバー構造
[V4L2] V4L2 サブデバイス



v4l2 サブデバイス ユーザー空間 API

ユーザーがサブデバイスのハードウェアを直接操作できるように、/devフォルダーの下にデバイス ノードを作成できます。v4l-subdevXユーザー空間にデバイス ノードを作成する必要がある場合は、サブデバイス ノードの登録前にフラグを設定し、V4L2_SUBDEV_FL_HAS_DEVNODEユーザーv4l2_device_register_subdev_nodes()空間にデバイス ノードを作成する関数を呼び出す必要があります。デバイス ノードはサブデバイス ノードの登録時に自動的に破棄されます。 -デバイスがアンロードされています。

    VIDIOC_QUERYCTRL
    VIDIOC_QUERYMENU
    VIDIOC_G_CTRL
    VIDIOC_S_CTRL
    VIDIOC_G_EXT_CTRLS
    VIDIOC_S_EXT_CTRLS
    VIDIOC_TRY_EXT_CTRLS

上記の ioctl は、デバイス ノードを通じてアクセスすることも、サブデバイス ドライバーで直接呼び出すこともできます。

   VIDIOC_DQEVENT
   VIDIOC_SUBSCRIBE_EVENT
   VIDIOC_UNSUBSCRIBE_EVENT

上記のイベントを使用するには、 のフラグ ビットを設定し、コールバック関数の関連するコールバック関数を実装する必要がありますv4l2_subdevV4L2_SUBDEV_USES_EVENTSこれcore_opssubscribeイベントを初期化してから を登録する必要がありますv4l2_subdev一部のプライベート ioctlv4l2_subdevは で実装できますops->core->ioctl

I2Cサブデバイスドライバー

I2C ドライバーにサポートを追加したい場合は、各 I2C インスタンス構造に構造を埋め込む必要があります。比較的単純な I2C デバイスの中には、カスタム状態構造を必要としないものもあります。現時点では、別の構造を作成するだけv4l2_subdevです。それ。一般的なドライバー定義の状態構造は次のとおりです。v4l2_subdevv4l2_subdev

    struct chipname_state {
    
    
        struct v4l2_subdev sd;
        ...  /* additional state fields */
    };

I2C サブデバイスの初期化に使用します。この関数は、 のすべてのメンバーv4l2_i2c_subdev_initを設定し、 と が相互にポイントしていることを確認します。インライン関数を追加して、ポインターから構造体を取得することもできます。v4l2_subdevv4l2_subdevi2c_clientv4l2_subdevi2c_client

struct i2c_client *client = v4l2_get_subdevdata(sd);
也可以从i2c_client结构体指针获取到v4l2_subdev结构体:
struct v4l2_subdev *sd = i2c_get_clientdata(client);
桥驱动可以使用以下帮助函数来创建一个I2C子设备:
struct v4l2_subdev *sd = v4l2_i2c_new_subdev
    (v4l2_dev, adapter,"module_foo", "chipid", 0x36, NULL);

この関数は、指定されたモジュール (空の場合もあります) をロードし、i2c_new_device渡されたパラメーターに従ってサブデバイス構造を作成するために呼び出し、最後にそれを登録しますv4l2_subdev


video_device 構造体

video_device動的に割り当てることができます。

struct video_device *vdev = video_device_alloc();
if (vdev == NULL)
    return -ENOMEM;
vdev->release = video_device_release;

video_device構造体をより大きな構造体に埋め込む必要がある場合は、vdev のリリース メンバーを設定する必要があります。カーネルは、次の 2 つのデフォルトのリリース コールバック関数を提供します。

video_device_release()       // 仅仅调用kfree释放分配的内存,用于动态分配情况下
video_device_release_empty() // 不做任何事情,静态变量

次の関数メンバーを設定する必要があります。

  • v4l2_dev: v4l2_device 親デバイスを指す必要があります

  • vfl_dir: VFL_DIR_RX (キャプチャデバイス)、VFL_DIR_TX (出力デバイス)、VFL_DIR_M2M (コーデックデバイス)

  • fops: v4l2_file_operations 構造体を設定します。

  • ioctl_ops: デバイス ノードを介してユーザー空間プログラムからアクセスできる ioctl。fops の .unlocked_ioctl を video_ioctl2 を指すように設定する必要があります。

  • lock: ドライバー空間でロック操作を実行する場合は、NULL に設定できます。それ以外の場合は、初期化された mutex_lock 構造体を指す必要があります。

  • Queue: vb2_queue 構造体を指します。queue->lock が空でない場合、キューに関連する ioctl はキュー内のロックを使用します。このようにして、他のタイプの ioctl 操作を待つ必要はありません。

  • prio: VIDIOC_G/S_PRIORITY で使用される優先度を追跡します。空の場合は、v4l2_device の v4l2_prio_state が使用されます。

  • dev_parent: v4l2_device を指すだけです

ioctl_opsioctl の 1 つを無視したい場合は、次の関数を呼び出すことができます。

   void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd);

これを に統合する場合は、内部のメンバーを設定して以下を提供するmedia_framework必要がありますvideo_devicemedia_entitymedia_pad

struct media_pad *pad = &my_vdev->pad;
int err;
err = media_entity_init(&vdev->entity, 1, pad, 0);
  • video_device の登録
    video_device の登録機能は以下のとおりです。
  err = video_register_device(vdev, VFL_TYPE_GRABBER, -1);

このコードは、キャラクター デバイス ドライバーを登録し、ユーザー空間にデバイス ノードを生成します。v4l2_device 親デバイスの mdev メンバーが空でない場合、video_device のエンティティはメディア フレームワークに自動的に登録されます。関数の最後のパラメータはデバイス ノードのインデックス番号で、-1 の場合は、最初のカーネルで使用可能なインデックス番号の値が使用されます。ユーザー空間に登録されているデバイス タイプとノード名は、次の識別子によって異なります。

VFL_TYPE_GRABBER: videoX 入出力デバイス
VFL_TYPE_VBI: vbiX
VFL_TYPE_RADIO: radioX ハードウェア定義オーディオ チューニング デバイス
VFL_TYPE_SDR: swradioX ソフトウェア定義オーディオ チューニング デバイス

デバイス ノードが作成されると、関連する属性も作成されます。その/sys/class/video4linux中にはこれらのデバイス フォルダーが表示されます。フォルダー内には、「name」、「dev_debug」、「index」、「uevent」などの属性が表示されます。 cat コマンドを使用して表示できます。「dev_debug」はビデオ デバイスのデバッグに使用できます。各ビデオ デバイスは「dev_debug」属性を作成します。この属性はフォルダの形式で存在し、以下で有効にすることができ/sys/class/video4linux/<devX>/ますlog file operation「dev_debug」はビットマスクであり、次のビットを設定できます。

  0x01:记录ioctl名字与错误码。设置0x08位可以只记录VIDIOC_(D)QBUF
  0x02:记录ioctl的参数与错误码。设置0x08位可以只记录VIDIOC_(D)QBUF
  0x04:记录file ops操作。设置0x08位可以只记录read&write成员的操作
  0x08:如上所示
  0x10:记录poll操作

上記のビットが設定されている場合、関連する呼び出しまたは操作が発生したときに、カーネルは関連する呼び出し情報を端末に出力します。に似ている

[173881.402120] ビデオ 4: VIDIOC_DQEVENT: エラー -2
[173884.906633] ビデオ 4: VIDIOC_UNSUBSCRIBE_EVENT

  • ビデオ デバイスのクリーンアップ ビデオ
    デバイス ノードを削除する必要がある場合、または USB デバイスが切断されている場合は、次の機能を実行する必要があります。
  video_unregister_device(vdev);

デバイスをアンインストールするには、この関数は /dev の下のデバイス ノード ファイルを削除します。同時に、media_entity_cleanupエンティティをクリーンアップするために呼び出しを忘れないでください。

ioctl とロック

V4L コア層はオプションのロック サービスを提供します。その中で最も重要なものは、ioctl を同期するために使用される video_device のロックです。videobuf2 フレームワークが使用されている場合、video_device->queue->lock ロックはキュー関連の ioctls 同期にも使用されます。異なるロックを使用すると、多くの利点があります。たとえば、一部の設定関連の ioctl には時間がかかります。独立したロックを使用すると、設定操作の完了を待たずに VIDIOC_DQBUF を実行できます。これは、ネットワーク カメラ ドライバーでは非常に一般的です。もちろん、ロック操作をドライバー自体で完全に完了することもでき、この場合、すべてのロック メンバーを NULL に設定し、ドライバー独自のロックを実装できます。

古い videobuf を使用する場合は、初期化関数video_deviceにロックを渡すvideobuf queue必要があります。videobuf がデータのフレームの到着を待っている場合、この時点でロックは一時的に解放され、ロックが完了すると再びロックされます。データが到着しないと、他の処理プログラムがそのデータにアクセスできなくなります。したがって、古い videobuf の使用はお勧めできません。videobuf2 フレームワークの下にある場合は、wait_prepare 与 wait_finishロックを解放または取得するコールバック関数を実装する必要があります。queue->lock を使用する場合は、V4L2 が提供するコールバック ヘルパー関数を使用して、ロックおよびロック解除の操作を完了できますvb2_ops_wait_prepare/finish。 queue->lock Lock を使用します (この時点でロックを初期化する必要があります)。

v4l2_fh 構造体

この構造は、ファイル ハンドル固有のデータを保存する簡単な方法を提供します。V4L2_FL_USES_V4L2_FHv4l2_fh-v4l2 フレームワークのユーザーは、関数 v4l2_fh_init を呼び出すことで設定されるvideo_device->flags のビットを確認することで、ドライバーが v4l2_fh を file->private_data ポインターとして使用するかどうかを知ることができます。

v4l2_fh 構造体はドライバー独自のファイル ハンドルとして存在し、ドライバーの open 関数で file->private_data がそれを指すように設定されています。複数の v4l2_fh がある場合、それらはリンク リストとして file->private_data に存在し、走査できます。そしてアクセスされました。ほとんどの場合、v4l2_fh 構造体はより大きな構造体に埋め込まれています。この場合、v4l2_fh_init+v4l2_fh_add を open 関数で呼び出して追加する必要があり、v4l2_fh_del+v4l2_fh_exit を release 関数で呼び出して終了する必要があります
ドライバーは、次に示すように、container_of を使用して独自のファイル ハンドル構造にアクセスできます。

struct my_fh {
    
    
    int blah;
    struct v4l2_fh fh;
};

int my_open(struct file *file)
{
    
    
    struct my_fh *my_fh;
    struct video_device *vfd;
    int ret;

    my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);

    v4l2_fh_init(&my_fh->fh, vfd);

    file->private_data = &my_fh->fh;
    v4l2_fh_add(&my_fh->fh);
    return 0;
}

int my_release(struct file *file)
{
    
    
    struct v4l2_fh *fh = file->private_data;
    struct my_fh *my_fh = container_of(fh, struct my_fh, fh);

    v4l2_fh_del(&my_fh->fh);
    v4l2_fh_exit(&my_fh->fh);
    kfree(my_fh);
    return 0;
}

上記のコードに示されているように、open 関数は複数のアプリケーションから呼び出される可能性があるため、複数の fh が存在しますが、file->private は常に最新の v4l2_fh を指しており、この v4l2_fh を通じてすべての要素を見つけることができます。一部のドライバーは、最初のファイル ハンドルが開かれた後、最後のファイル ハンドルが閉じる前に、他の作業を行う必要があります。次の 2 つのヘルパー関数は、v4l2_fh 構造体にエントリが 1 つだけ残っているかどうかを確認できます。

int v4l2_fh_is_singular(struct v4l2_fh *fh)
如果是只有一个entry,返回1,否则返回0,如果fh为空也返回0int v4l2_fh_is_singular_file(struct file *filp)
和上面差不多,但是使用 filp->private_data 这一数据源,实际上它是指向最新的一个v4l2_fh的。

V4L2 イベント

V4L2 eventsユーザー空間にイベントを配信する共通の方法を提供するには、ドライバーは v4l2_fh (video_device のフラグ ビットを設定) を使用して V4L2 イベントをサポートする必要があります。イベントは識別識別子としてタイプと ID を使用し、未使用のイベントの ID は 0 です。

ユーザーがイベントをサブスクライブすると、ユーザー空間はそれに応じて kevent 構造体を各イベントに割り当てます (elems パラメーターが 0 の場合は 1 つだけ、0 でない場合は指定された数に従って割り当てられます)。 , したがって、各イベントには 1 つ以上の独自の A kevent 構造があり、ドライバーが短期間に多数のイベントを生成した場合に、同じタイプの他のイベントをカバーしないことが保証されます。さまざまな種類の果物を入れるためのいくつかのバスケット。イベント構造体はv4l2_subscribed_event構造体の最後のメンバーです。配列の形式で存在し、柔軟な配列 (struct v4l2_kevent events[]) です。つまり、構造体のスペースが割り当てられると、イベントはスペースを占有せずv4l2_subscribed_event、追加のスペースが必要です。指定した数のイベントにスペースを割り当てます。kzalloc(siezof(struct v4l2_subscribed_event) + sizeof(struct v4l2_kevent) * num, GFP_KERNEL);これを使用すると、kevents を配列でアドレス指定できるため、非常に便利です。

取得したイベント数が kevent を超える場合、古いイベントは破棄されます。構造体のマージおよび置換コールバック関数を設定できますv4l2_subscribed_event(実際には、デフォルト関数で十分です)。これらの関数は、イベントがキャプチャされ、イベントを保存するスペースがなくなったときに呼び出されます。( ) と() がコールバック関数として使用されるv4l2_event.c置換/マージの良い例があります。これら 2 つの関数は割り込みコンテキストで呼び出すことができるため、すぐに実行して返す必要があります。ctrls_replacectrls_merge

イベントに関するループはさらに興味深い操作で、キューに入るとき: 3 つの変数 (first - デキューされる次のイベント、elems - kevents の総数
、in_use - すでに使用されている kevents の数)

elems == in_use の場合、キュー メンバーが使い果たされていることを意味します。

最初の kevent を取り出し、使用可能なキューから削除します。最初は配列の次のメンバー in_use — を指します。

前のステップを見つけて (最初は配列の次のメンバーを指します)、前のステップの変更ビット (最初の kevent を削除) をマージして前のステップに割り当てます。
後者は前者より新しいため、値は前者の変更を保持しながら前者を完全にカバーできます。


新しい埋め込み項目として、配列 kevent item in_use + first >= elems ? in_use + first - elems : in_use + first; を取り出します。

使用中++

いくつかの便利な機能:

int v4l2_event_subscribe(struct v4l2_fh *fh, struct v4l2_event_subscription *sub, 
        unsigned elems, const struct v4l2_subscribed_event_ops *ops)

ユーザー空間が ioctl を通じてサブスクリプション リクエストを開始するとき、video_device->ioctl_ops->vidioc_subscribe_event
リクエストされたイベントがサポートされているかどうかを確認する必要があり、サポートされている場合は、上記の関数を呼び出してサブスクライブします。一般に、ビデオ関連の ioctl をカーネルのデフォルトv4l2_ctrl_subscribe_event() 関数に指定できます。

int v4l2_event_unsubscribe(struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
取消一个事件的订阅,V4L2_EVENT_ALL类型可以用于取消所有事件的订阅。一般可以将video的相关ioctl指向该函数。

void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
该函数用作events入队操作(由驱动完成),驱动只需要设置type以及data成员,其余的交由V4L2来完成。

int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
               int nonblocking)
events出队操作,发生于用户空间的VIDIOC_DQEVENT调用,作用是从available队列中取出一个events。

v4l2_subscribed_event_opsパラメーターを使用すると、ドライバーは次の 4 つのコールバック関数メンバーを設定できます。

  • add: イベントサブスクリプションを追加するときに呼び出されます

  • del: イベントサブスクリプションをキャンセルするときに呼び出されます

  • replace: イベントは古いものを新しいものに置き換えます。キューがいっぱいになったときに呼び出されます。以下同様です。要素が 1 つしかないときに kevent.u.ctrl 項目をコピーするためによく使用されます。

  • merge: 古いイベントを新しいイベントにマージします。複数の要素に使用すると、変更項目のみがマージされます。理由については、上記のイベント ループ処理の説明を参照してください。

イベントは、ドライバーがパラメーターv4l2_fh->waitとして使用できるポーリング システム コールを通じてユーザー空間に渡されます。poll_wait()サブデバイスは、V4L2_DEVICE_NOTIFY_EVENTnotify 関数を通じてイベントを ( を使用して) v4l2_device に直接送信できます。drivers/media/platform/omap3ispイベントの使用方法の例を示します。

予防:

v4l2_event_subscribe の elems パラメータに注意してください。それが 0 の場合、カーネルはデフォルトで 1 を割り当てます。それ以外の場合は、指定されたパラメータ値に従って割り当てられます。

カーネルのデフォルトの v4l2_subscribed_event_ops は使用しないことをお勧めします。その追加関数は、v4l2_ctrl 内の対応する ID の Ctrl を見つけようとします。カスタム イベント ID の場合、関連する Ctrl 項目が見つからない可能性があります。この場合は
、 、ユーザー空間の VIDIOC_SUBSCRIBE_EVENT は失敗を返します。

ユーザー空間の dqevent の後は、カーネルが使用された kevent を自動的に取得し、分散したリンク リストではなく柔軟な配列で管理するため、返却操作を気にする必要はありません。

サブデバイスはイベントをキューに入れ、v4l2_subdev_notify_event 関数を呼び出すことで v4l2 デバイスの通知コールバックに通知できます。

v4l2_event_queue 関数は、video_device 上のすべての v4l2_fh を走査し、イベントを各 fh のリストにキューに入れます。fh
は、ユーザーがビデオ デバイス ノードを開いたときに生成され、各ユーザーがビデオ ノードを開いたときに個別の v4l2_fh が割り当てられます。

file->private は常に最新の v4l2_fh を指し、この v4l2_fh を通じて、v4l2_fh リンク リスト全体のすべての要素を見つけることができます。

v4l2_fh_release 関数は、fh にマウントされているすべてのイベントのサブスクライブを解除します。

おすすめ

転載: blog.csdn.net/qq_44710568/article/details/132604943