[Bases de Linux] - Périphérique framework-media V4L2

Cet article donne une introduction détaillée à la gestion des périphériques de flux de données d'exécution de V4L2, y compris ce que l'on appelle [gestion des périphériques d'exécution], à quoi sert-elle, comment l'utiliser, etc. Le but de cet article est de maîtriser l'utilisation du codage et l'application fonctionnelle de l'appareil meida.

一 、 cadre des médias

1.1 Introduction

L'API de contrôle associée se trouve dans Documentation / DocBook / media / v4l / media-controller.xml. Ce document se concentre sur l'implémentation du framework de support de test principal. Remarque: vous ne pouvez rien voir en le visualisant directement. Faire des htmldocs ou d'autres choses dans le répertoire racine du noyau fera l'affaire. C'est facile à voir.

1.2. Équipement de contrôle au moment de l'exécution

Autrement dit, le contrôle de la ligne de flux de données après le démarrage du périphérique, tout comme une ligne d'assemblage d'usine, chaque nœud (étiquetage, impression et emballage) sur la ligne d'assemblage est comme chaque sous-périphérique du périphérique d'entrée, et le contrôle de l'appareil au moment de l'exécution est pour obtenir l'effet de pouvoir contrôler le nœud, par exemple, il existe plusieurs machines pour l'étiquetage, laquelle doit être sélectionnée pour ce traitement de pipeline, s'il faut ajouter l'écran en soie, quelle machine ajouter, etc.

1.3, rôle

Fournir une gestion de pipeline en temps réel. Le pipeline est compris comme un pipeline. Imaginez une conduite d'eau. L'eau à l'intérieur est le flux de données. La vidéo csi-> isp-> dans le périphérique d'entrée forme une ligne de pipeline. L'infrastructure multimédia fournit des fonctions telles que l'ouverture, la fermeture, le contrôle des effets et le contrôle des nœuds du pipeline.

1.4 Comment utiliser

Le noyau utilise principalement quatre structures pour organiser de nombreux nœuds: media_device, media_entity, media_link et media_pad. L'ensemble du cadre médiatique est utilisé autour de ces quatre structures, qui seront présentées en détail ci-dessous.

1.5, modèle d'appareil abstrait

L'un des objectifs de la structure multimédia est de découvrir la topologie de l'appareil et de la configurer au moment de l'exécution. Afin d'atteindre cet objectif, le framework multimédia extrait les périphériques matériels en entités, qui sont connectées via des liens.

1. Entité: abstraction du module de périphérique matériel (analogue aux divers composants et puces de la carte de circuit imprimé)

2, pad: abstraction de port de périphérique matériel (composants analogiques, broches sur la puce)

3. Lien: L'abstraction de connexion du périphérique matériel, les deux extrémités du lien sont des plots (connexions entre les broches des composants analogiques)

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

Imaginez que si vous avez besoin d'établir une connexion entre des entités, vous devez stocker des informations sur le lien et l'entité dans le bloc. Le lien doit stocker les informations du bloc et de l'entité, et l'entité doit stocker les informations du lien et du bloc. Il appartient à toi et moi, dans ton cas.

Deux, équipement multimédia

Un périphérique multimédia est représenté par une structure media_device. Normalement, la structure doit être intégrée dans une structure définie par périphérique plus grande, et la plupart du temps, media_device et v4l2_device sont à un niveau parallèle, ou le code de omap3isp est un exemple:

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. Utilisez la fonction suivante pour enregistrer le périphérique multimédia:

media_device_register (struct media_device * mdev);

L'appelant de la fonction doit définir les membres de structure suivants avant l'enregistrement (il est de la responsabilité de l'appelant d'initialiser la structure à l'avance):

  • dev: Doit pointer vers un appareil parent, généralement le membre de l'appareil de l'appareil de la plate-forme.
  • model: le nom du modèle.

Les membres suivants sont facultatifs:

  • serial: numéro de série, doit être unique
  • bus_info: informations sur le bus, s'il s'agit d'un périphérique PCI, il peut être défini sur "PCI:"
  • hw_revision: version matérielle. Si possible, il doit être formaté avec la définition de macro KERNEL_VERSION
  • driver_version: version du pilote. Le nom du nœud de périphérique final est media [0-9], et le numéro de nœud est automatiquement généré par le noyau.

2.2. Utilisez la fonction suivante pour désinstaller l'appareil

media_device_unregister (struct media_device * mdev);

Il convient de noter que la désinstallation d'un appareil qui n'a pas été enregistré est *** dangereuse ***. Il y a plusieurs raisons principales pour lesquelles la supposition du code de visualisation personnel n'est pas sûre:

  1. S'il n'est pas enregistré, l'entité membre à l'intérieur de media_device peut ne pas être initialisée. Si sa valeur est une valeur indéterminée, cela entraînera un accès illégal;
  2. S'il n'est pas enregistré, les membres devnode internes ne seront pas initialisés et des problèmes se produiront lors de la désinstallation.

三 、 entités 、 pads 、 liens

3.1 、 entités

Les entités sont représentées par une structure media_entity, qui est généralement intégrée dans une structure plus grande, telle que la structure v4l2_subdev ou video_device (vous n'avez pas besoin d'allouer de l'espace par vous-même, la structure est déjà incluse), bien sûr, vous pouvez également allouer directement un entité. Utilisez la fonction suivante pour initialiser l'entité:

media_entity_init (entité struct media_entity *, u16 num_pads, struct media_pad * pads, u16 extra_links);

Les paramètres à noter avant d'exécuter la fonction d'initialisation sont:

  • num_pads: le nombre de pads, qui est lié à la structure du sous-périphérique pilote;
  • pads: tableau de structure media_pad, généralement le pad est intégré dans la structure définie par le pilote, l'adresse du tableau est passée à ce paramètre et le pad doit être initialisé à l'avance;
  • extra_links: Cette fonction alloue le nombre de liens en fonction de num_pads, ce paramètre indique combien de liens supplémentaires sont nécessaires en plus du nombre pré-alloué;
  • entity: Le nom, le type, les indicateurs, la révision et l'identifiant de groupe de media_entity doivent être définis avant ou après l'initialisation. Si la structure est intégrée dans une structure de niveau supérieur, ces membres peuvent également être définis par un code de structure de niveau supérieur. id est renseigné lors de l'enregistrement (si le membre id est défini à l'avance, la valeur prédéfinie sera conservée lors de l'enregistrement). L'entité a des indicateurs associés [indicateurs] pour identifier son statut et sa fonction, MEDIA_ENT_FL_DEFAULT signifie qu'il s'agit d'une entité par défaut. Vous pouvez définir l'ID de groupe de plusieurs entités sur le même entier pour identifier qu'elles appartiennent à la même catégorie. Pour le noyau, l'ID de groupe est inutile, mais l'ID de groupe sera transmis à l'espace utilisateur lorsque l'entité est énumérée. Utile dans certaines situations de l'espace utilisateur.

3.2 coussinets 、

Le pad est représenté par une structure media_pad, et les données des pads sont gérées par le pilote (sous forme de tableau). Les tampons utilisent des indices d'entité et de tableau pour une identification unique. L'identifiant à l'intérieur de l'entité ne sera pas répété, mais l'identifiant du tampon entre différentes entités peut être répété, de sorte que l'index du tampon doit être confirmé conjointement par l'entité et l'identifiant.

Étant donné que le nombre de pads est connu à l'avance (la puce que vous fabriquez, vous devez savoir combien de broches elle possède), la structure media_pad n'est plus allouée dynamiquement, et le pilote devrait être responsable de la gestion du tableau de structure (éviter l'allocation dynamique) . Le pilote doit définir l'attribut direction des pads avant que la fonction media_entity_init ne soit appelée. Les pads ont des bits indicateurs pour indiquer ses attributs. Vous n'avez besoin de définir ce membre que lors de l'initialisation, et le reste est fait par la fonction media_entity_init:

MEDIA_PAD_FL_SINK:    目的 pad
MEDIA_PAD_FL_SOURCE:  源 pad

3.3 、 liens

Les liens sont représentés par une structure media_link. Tous les plots de chaque entité stockent tous les liens qui lui sont liés. Un lien sera stocké respectivement par le pavé source et le pavé de destination, de manière à réaliser la traversée dans les deux sens positif et négatif . Utilisez la fonction suivante pour créer des liens:

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

Les liens ont quelques bits de drapeaux pour identifier ses attributs:

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

Contrairement aux pads, le nombre de liens n'est pas toujours confirmé à l'avance (parfois sur le circuit imprimé, vous ne pouvez pas confirmer complètement combien de périphériques doivent être connectés aux broches, il est très probable qu'il y aura des changements temporaires), donc la fonction media_entity_init est basé sur Les paramètres entrants pré-allouent une certaine quantité de structure media_link, si cela ne suffit pas, il sera alloué dynamiquement dans media_entity_create_link (si le nombre de liens est supérieur ou égal à max_link, le nombre de liens sera élargi ).

Quatre, enregistrement et désinstallation

Le pilote doit utiliser les fonctions suivantes pour enregistrer et désinstaller l'entité (n'a pas besoin d'être exécuté manuellement, cela se fait dans la fonction 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);

Le noyau utilise un entier positif unique pour représenter chaque entité (unique sous le même media_device), et le pilote peut également spécifier la valeur d'ID de l'entité en remplissant le membre media_entity-> id, mais il doit être unique. Si les ID sont générés automatiquement par le noyau, il n'y a aucune garantie qu'ils soient continus. En fait, les ID générés automatiquement par le noyau sont attribués par le media_device-> entity_id ++ de l'entité, et la valeur est initialisée à 1 dans le Media_device_register, fonction Liman.

Après avoir déchargé l'entité, vous devez appeler les fonctions suivantes pour libérer les ressources associées demandées, principalement pour libérer la mémoire de structure media_link allouée dynamiquement:

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

Pour traverser des entités, vous pouvez effectuer l'appel système MEDIA_IOC_ENUM_ENTITIES dans l'espace utilisateur. Vous devez définir l'ID de media_entity_desc sur (0 | MEDIA_ENT_ID_FLAG_NEXT). Pendant la boucle, il vous suffit de définir id | = MEDIA_ENT_ID_FLAG_NEXT pour terminer le processus de traversée d'entités. Si vous avez besoin d'énumérer l'entité spécifiée, vous devez définir l'id sur la valeur d'id de l'entité spécifiée (l'id de l'entité du noyau commence à 1), cela peut être vu dans la fonction d'enregistrement d'entité. L'exemple de code est le suivant, j'ai essayé de rationaliser le code publié pour éviter d'occuper trop d'espace:

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. Parcours du graphe (profondeur en premier)

Que fait la traversée de graphe? Il nous fournit un moyen d'accéder à chaque entité spécifiée au moment de l'exécution. Quant à la raison pour laquelle nous avons besoin d'un accès, c'est parce que nous pouvons avoir besoin de les gérer au moment de l'exécution .

Les fonctions suivantes peuvent être utilisées pour parcourir les entités appartenant au même périphérique média (parcours linéaire, parcours de graphe atypique, c'est-à-dire la même méthode de parcours que la liste chaînée):

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

Le pilote peut avoir besoin de parcourir toutes les entités accessibles à partir d'une entité donnée via les liens activés. L'infrastructure multimédia fournit une API en profondeur pour accomplir cette tâche. Il est à noter qu'il faut éviter de parcourir le graphe en boucle fermée, sinon il tombera dans une boucle infinie. Pour éviter cette situation, la fonction limite la profondeur de parcours maximale à MEDIA_ENTITY_ENUM_MAX_DEPTH. La dernière définition de cette macro est 16 ** [À partir de 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);

Lors de l'utilisation, commencez par initialiser le graphe avec la première fonction, puis appelez la seconde fonction pour effectuer un parcours en boucle. Une fois le parcours terminé, la seconde fonction renverra NULL. Le processus de traversée peut être interrompu à tout moment et il n'est pas nécessaire d'appeler la fonction de nettoyage.

Il existe une fonction d'assistance correspondante pour trouver le lien de deux pads donnés, ou pour trouver un autre pad connecté à celui-ci via un pad.

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

Six, complétez les paramètres des liens

Les attributs du lien peuvent être modifiés au moment de l'exécution, il suffit d'appeler la fonction suivante pour terminer:

media_entity_setup_link(struct media_link *link, u32 flags);

Le paramètre flags est utilisé pour définir les attributs du lien spécifié. Les attributs autorisés vont de l'attribut MEDIA_LINK_FL_ENABLED à l'indicateur MEDIA_LINK_FL_ENABLE ou MEDIA_LINK_FL_DISABLE. Si le lien est défini avec l'indicateur MEDIA_LINK_FL_IMMUTABLE, il ne peut pas être activé ou désactivé. Lorsqu'un lien est activé ou fermé, la structure multimédia appelle le récepteur et le link_setup du côté source en deux appels pour les paramètres associés. Si le deuxième appel échoue, le premier appel sera également restauré.

Le pilote de périphérique multimédia peut définir media_device-> link_notify pour pointer vers une fonction de rappel, qui sera appelée une fois l'opération link_setup terminée. Si un lien n'est pas immuable, le pilote d'entité doit implémenter l'opération link_setup par lui-même. La configuration d'un lien ne devrait pas affecter les autres liens. Si un lien est connecté au bloc récepteur et que le lien est activé, les autres liens vers le bloc ne peuvent plus être activés et -EBUSY doit être renvoyé à ce moment.

6.1 Style de média donné par pipeline

Le concept de pipeline a déjà été introduit et ne sera pas répété. Voici un schéma explicatif (en fait, ce n'est pas très clair et facile à comprendre, et il y en a plus clairs et faciles à comprendre qui ne peuvent pas être publiés pour certaines raisons . Veuillez utiliser votre imagination, selon la figure. La description ci-dessus résume une image en elle-même, tant qu'elle ne se concentre que sur un point - le pipeline est l'abstraction des liens de flux de données, je crois que vous):

Lorsque la diffusion en continu est activée, le pilote doit notifier toutes les entités du pipeline pour maintenir l'état actuel inchangé, et vous pouvez appeler la fonction suivante pour terminer la notification (cette fonction n'appellera que le rappel de validation du côté récepteur):

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

Cette fonction marquera toutes les entités de la connexion d'activation du lien comme état de diffusion, que ce soit directement ou indirectement. La structure media_pipeline pointée par le deuxième paramètre sera transmise à toutes les entités du pipeline. Le pilote doit intégrer la structure media_pipeline dans une structure de niveau supérieur et peut accéder au pipeline à partir de la structure media_entity. Lorsque vous devez arrêter la diffusion, vous devez appeler:

media_entity_pipeline_stop(struct media_entity *entity);

Étant donné que la fonction de démarrage peut être appelée de manière imbriquée, ce qui lui correspond, la fonction d'arrêt doit également conserver un nombre d'appels correspondant. La fonction media_entity_pipeline_start vérifiera la validité du lien. À ce stade, le membre link_validate de media_entity sera utilisé pour terminer la vérification. L'image suivante est une illustration de la traversée:

                                                                                                     traversée du pipeline

Le numéro du cercle dans la figure ci-dessus fait référence à l'ordre des visites. Il est à noter que l'implémentation par le noyau de la traversée de graphe en largeur d'abord est très intrigante et de valeur de référence, et il vaut la peine de trouver le code du noyau par vous-même. Parmi eux, des concepts tels que les piles et les bitmaps sont utilisés. Le code spécifique se trouve dans la fonction media-entity.c / media_entity_pipelien_start.

Puisque la traversée ci-dessus est la largeur d'abord et toute la traversée, c'est-à-dire si votre FAI a deux sources d'entrée, alors ces deux sources d'entrée peuvent être activées en même temps, le plus souvent nous ne voulons pas que cela se produise. attendez-vous à ce que la source d'entrée spécifiée et le pipeline à liaison unique spécifié soient ouverts. À ce stade, vous devez gérer vous-même le pipeline et vous pouvez implémenter votre propre structure de pipeline pour la gestion associée. La mise en œuvre est très simple, cette structure de pipeline est similaire à la suivante:

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);
    ... ...
};

Des astuces:

1. La recherche de v4l2_subdev à partir de media_entity peut être effectuée en utilisant la fonction media_entity_to_v4l2_subdev.

2. Vous pouvez ajouter l'indicateur d'activation du lien dans la partie streamon du module vidéo. La fonction media_entity_pipeline_start ajoutera de la valeur au membre entity.stream_count et transmettra le tube dans le second paramètre au membre entity.pipe sur la ligne de liaison. Une fois l'indicateur d'activation terminé, la traversée du graphe peut être effectuée sur chaque entité pour appeler son membre set_stream pour démarrer le flux. En fait, lorsque s_param, s_fmt et d'autres ioctl sont appelés, la traversée du graphe est nécessaire pour appeler les fonctions de rappel de chaque entité sur l'ensemble du pipeline pour les paramètres associés.

3. Une fois que tous les nœuds de sous-périphérique sont enregistrés, la connexion de chaque entité peut être effectuée via media_entity_create_link, en attendant que tout le flux de données soit ouvert ultérieurement.

4. Pourquoi existe-t-il un cadre médiatique? Comme il n'y a qu'un sous-périphérique, chaque classe de sous-périphérique est dans un état de niveau et il n'y a aucune différence dans la direction du flux de données. Si vous devez établir un pipeline de flux de données, vous devez l'implémenter vous-même, ce qui sera plus gênant. Avec le framework média, ces gestions Ce sera beaucoup plus pratique, car il fournit toutes les opérations de gestion du pipeline, de sorte que de nombreux sous-périphériques peuvent être connectés en série comme un flux de données complet, de manière à effectuer correctement et transfert de données dirigé.

5. Les deux v4l2_subdev et video_device ont une structure media_entity (de type sans pointeur). L'entité dans video_devcie sera enregistrée avec l'enregistrement du périphérique vidéo et le nom sera le nom du périphérique vidéo. Les deux sont différents. Lorsqu'il est utilisé Besoin spécial attention.

Je suppose que tu aimes

Origine blog.csdn.net/u014674293/article/details/111488115
conseillé
Classement