Media Pads And Links Explained-Free Linux Device Drivers Course

PREV_LEC  |  NEXT_LEC

Media Pads And Links Explained

Free Linux Kernel Development Course — struct media_pad, struct media_link, backlinks, and registering entities with the media device

free linux kernel development course media pads and links free linux device drivers course free embedded systems course

We now have entities and a media device that owns them. What we’re still missing is the actual wiring: pads as connection points, and links as the point-to-point connections between them. This lecture in our free linux kernel development course covers struct media_pad and struct media_link in full, explains the somewhat confusing backlink mechanism, and walks through registering a complete two-entity pipeline in an original driver.

What You Will Learn

  • The fields of struct media_pad and how pad flags work
  • The fields of struct media_link, including the MEDIA_LNK_FL_* flag family
  • Why every link is actually stored twice — once as a link, once as a backlink
  • media_entity_pads_init(), entity registration, and entity cleanup APIs

Prerequisites

This lecture builds directly on the previous one covering struct media_device and struct media_entity — read that first if you haven’t already.

struct media_pad: A Connection Endpoint

A pad is defined by struct media_pad, and every entity owns an array of these, sized by its num_pads field:

struct media_pad {
    struct media_entity *entity;
    u16 index;
    unsigned long flags;
};
  • entity — a back-pointer to the owning entity.
  • index — the pad’s zero-based position inside that entity’s pad array.
  • flags — either MEDIA_PAD_FL_SINK or MEDIA_PAD_FL_SOURCE, never both, since a pad can only sink data or source it, never do both simultaneously. A commonly paired flag, MEDIA_PAD_FL_MUST_CONNECT, tells the framework that streaming cannot validly start unless this particular pad has an active link.

A pad is identified purely by the combination of its owning entity plus its index — there is no separate global pad ID a driver needs to track.

struct media_link: The Wire Between Two Pads

Two pads — from the same entity or from two different entities — are joined together by a struct media_link:

struct media_link {
    struct media_gobj graph_obj;
    struct list_head list;
    struct media_pad *source;
    struct media_pad *sink;
    struct media_link *reverse;
    unsigned long flags;
    bool is_backlink;
};
  • list — associates this link with whichever entity or interface owns it.
  • source — the pad the link originates from.
  • sink — the pad the link terminates at.
  • reverse — a pointer to the corresponding backlink, explained below.
  • is_backlink — true if this particular link struct is the backlink copy rather than the original.
  • flags — the MEDIA_LNK_FL_* family:
    • MEDIA_LNK_FL_ENABLED — the link is active and ready to carry data.
    • MEDIA_LNK_FL_IMMUTABLE — the link’s enabled state can never change at runtime; typically used for hard-wired, single-purpose connections.
    • MEDIA_LNK_FL_DYNAMIC — the link’s state can change while streaming is active. Drivers may set this, but it is read-only from user space’s point of view.

Why Every Link Is Stored Twice

This is the part most people find confusing the first time: each entity keeps a list of every link that touches any of its pads. Since a link connects two entities, that means a single logical connection is actually stored twice — once against the source entity, once against the sink entity. When you connect entity A to entity B, the framework creates:

  • The forward link, stored in A’s link list, with A’s num_links incremented.
  • The backlink, stored in B’s link list, with the same source and sink pads, but is_backlink set to true. B’s num_backlinks and num_links are both incremented, and this backlink is assigned to the original link’s reverse pointer.

topology_version on the owning media device is incremented twice as a result — once for each side. Think of the backlink simply as a backup reference that lets the framework traverse the graph from either direction without having to search every entity’s link list.

Link vs Backlink

[Entity-A source pad] –link–> [Entity-B sink pad]
[Entity-A source pad] <–backlink– [Entity-B sink pad]
Same source/sink pads both times; only is_backlink differs

Registering Pads And Entities

A single API initializes both an entity and its pad array in one call:

int media_entity_pads_init(struct media_entity *entity,
                            u16 num_pads, struct media_pad *pads);

The driver must set every pad’s flags field before calling this. Here is an original two-entity example — a sensor entity with a single source pad, feeding a scaler entity with one sink and one source pad — inside a driver called ep_pipeline.

struct ep_pipeline_dev {
    struct device *dev;
    struct media_device mdev;

    struct media_entity sensor_entity;
    struct media_pad sensor_pad;

    struct media_entity scaler_entity;
    struct media_pad scaler_pads[2];
};

static int ep_pipeline_probe(struct platform_device *pdev)
{
    struct ep_pipeline_dev *epp;
    int ret;

    epp = devm_kzalloc(&pdev->dev, sizeof(*epp), GFP_KERNEL);
    if (!epp)
        return -ENOMEM;

    epp->dev = &pdev->dev;
    epp->mdev.dev = &pdev->dev;
    strscpy(epp->mdev.model, "ep-pipeline", sizeof(epp->mdev.model));
    media_device_init(&epp->mdev);

    epp->sensor_entity.name = "ep-sensor";
    epp->sensor_entity.function = MEDIA_ENT_F_CAM_SENSOR;
    epp->sensor_pad.flags = MEDIA_PAD_FL_SOURCE;
    ret = media_entity_pads_init(&epp->sensor_entity, 1, &epp->sensor_pad);
    if (ret)
        return ret;

    epp->scaler_entity.name = "ep-scaler";
    epp->scaler_entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
    epp->scaler_pads[0].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
    epp->scaler_pads[1].flags = MEDIA_PAD_FL_SOURCE;
    ret = media_entity_pads_init(&epp->scaler_entity, 2, epp->scaler_pads);
    if (ret)
        return ret;

    ret = media_device_register_entity(&epp->mdev, &epp->sensor_entity);
    if (ret)
        return ret;

    ret = media_device_register_entity(&epp->mdev, &epp->scaler_entity);
    if (ret)
        return ret;

    ret = media_create_pad_link(&epp->sensor_entity, 0,
                                 &epp->scaler_entity, 0,
                                 MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
    if (ret)
        return ret;

    ret = media_device_register(&epp->mdev);
    if (ret)
        return ret;

    dev_info(epp->dev, "ep-pipeline registered, sensor num_links=%u scaler num_backlinks=%u\n",
             epp->sensor_entity.num_links, epp->scaler_entity.num_backlinks);

    return 0;
}

Loading this module and checking media-ctl output confirms the two-entity, one-link graph:

$ sudo insmod ep_pipeline.ko
$ dmesg | tail -2
[  55.201004] ep_pipeline platform: ep-pipeline registered, sensor num_links=1 scaler num_backlinks=1

$ media-ctl -d /dev/media0 -p
Entity 1: ep-sensor (1 pad, 1 link)
        type V4L2 subdev subtype Sensor
        pad0: Source
                -> "ep-scaler":0 [ENABLED,IMMUTABLE]

Entity 2: ep-scaler (2 pads, 1 link)
        type V4L2 subdev subtype Unknown
        pad0: Sink
                <- "ep-sensor":0 [ENABLED,IMMUTABLE]
        pad1: Source

Exactly as expected: the sensor’s num_links is 1 (the forward link it owns), and the scaler’s num_backlinks is 1 (the backlink stored on its side of the same connection).

Unregistering And Cleaning Up

Drivers that need to tear down entities explicitly call:

media_device_unregister_entity(struct media_entity *entity);
media_entity_cleanup(struct media_entity *entity);

In practice this is rarely needed by hand — when the whole media device is unregistered via media_device_unregister(), every entity it owns is unregistered automatically, so manual per-entity unregistration is really only needed for entities that come and go independently of the parent device’s own lifecycle.

Common Mistakes To Avoid

  • Calling media_create_pad_link() before both entities have been registered with media_device_register_entity() — the link creation call needs both entities to already exist in the graph.
  • Forgetting MEDIA_PAD_FL_MUST_CONNECT on a pad that genuinely requires an active link before streaming can start — without it, the framework won’t reject an invalid, disconnected configuration.
  • Assuming num_backlinks should ever equal num_links for every entity — that’s only true for entities with exactly one link on each side; most entities have an asymmetric mix.

Best Practices

  • Register every entity before creating any links between them — it keeps the failure modes predictable and matches how the kernel’s own drivers are structured.
  • Use MEDIA_LNK_FL_IMMUTABLE for connections that are truly fixed by hardware design, and reserve MEDIA_LNK_FL_DYNAMIC only for genuinely switchable routing.
  • Verify your topology early with media-ctl -p during development rather than trusting your mental model of the graph.

Summary

A pad is a typed connection point on an entity; a link ties one source pad to one sink pad and is always stored on both entities involved — once as the forward link, once as the backlink. media_entity_pads_init(), media_device_register_entity(), and media_create_pad_link() are the three calls that actually build a working graph. Next in this free linux device drivers course, we cover media_entity_operations and how entities can validate or react to their own links.

FAQ

Why is a link stored twice, once on each entity?

So the framework can traverse the graph efficiently from either entity’s side without having to search every other entity’s link list. The copy on the sink entity’s side is called a backlink.

What does MEDIA_PAD_FL_MUST_CONNECT actually enforce?

It tells the framework that this pad requires an active, enabled link before a streaming pipeline through it can be considered valid.

Can a link’s enabled state change while streaming?

Only if it’s flagged MEDIA_LNK_FL_DYNAMIC. Links flagged MEDIA_LNK_FL_IMMUTABLE can never change state.

Do I need to call media_entity_cleanup() manually?

Usually not — unregistering the whole media device automatically unregisters and cleans up every entity it owns. Manual cleanup is only for entities with an independent lifecycle.

In what order should entities and links be created?

Register every entity with media_device_register_entity() first, then create links between them with media_create_pad_link().

Next: media_entity_operations and v4l2_subdev_pad_ops

Continue this free linux kernel development course and learn how entities validate their own links.

Next Lecture Browse Full Course

PREV_LEC  |  NEXT_LEC

Leave a Reply

Your email address will not be published. Required fields are marked *