diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h
index 3fcdb4b4d5f8..8d491a87867c 100644
--- a/src/libcamera/include/media_device.h
+++ b/src/libcamera/include/media_device.h
@@ -54,6 +54,8 @@ private:
 
 	std::vector<MediaEntity *> entities_;
 
+	struct media_v2_interface *findInterface(const struct media_v2_topology &topology,
+						 unsigned int entityId);
 	bool populateEntities(const struct media_v2_topology &topology);
 	bool populatePads(const struct media_v2_topology &topology);
 	bool populateLinks(const struct media_v2_topology &topology);
diff --git a/src/libcamera/include/media_object.h b/src/libcamera/include/media_object.h
index 65b55085a3b0..950a33286690 100644
--- a/src/libcamera/include/media_object.h
+++ b/src/libcamera/include/media_object.h
@@ -80,21 +80,28 @@ class MediaEntity : public MediaObject
 {
 public:
 	const std::string &name() const { return name_; }
+	unsigned int major() const { return major_; }
+	unsigned int minor() const { return minor_; }
 
 	const std::vector<MediaPad *> &pads() const { return pads_; }
 
 	const MediaPad *getPadByIndex(unsigned int index) const;
 	const MediaPad *getPadById(unsigned int id) const;
 
+	int setDeviceNode(const std::string &devnode);
+
 private:
 	friend class MediaDevice;
 
-	MediaEntity(const struct media_v2_entity *entity);
+	MediaEntity(const struct media_v2_entity *entity,
+		    unsigned int major = 0, unsigned int minor = 0);
 	MediaEntity(const MediaEntity &) = delete;
 	~MediaEntity();
 
 	std::string name_;
 	std::string devnode_;
+	unsigned int major_;
+	unsigned int minor_;
 
 	std::vector<MediaPad *> pads_;
 
diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
index 605e504be124..70b3fff3f492 100644
--- a/src/libcamera/media_device.cpp
+++ b/src/libcamera/media_device.cpp
@@ -208,6 +208,7 @@ int MediaDevice::populate()
 	struct media_v2_entity *ents = nullptr;
 	struct media_v2_link *links = nullptr;
 	struct media_v2_pad *pads = nullptr;
+	struct media_v2_interface *interfaces = nullptr;
 	__u64 version = -1;
 	int ret;
 
@@ -221,6 +222,7 @@ int MediaDevice::populate()
 		topology.ptr_entities = reinterpret_cast<__u64>(ents);
 		topology.ptr_links = reinterpret_cast<__u64>(links);
 		topology.ptr_pads = reinterpret_cast<__u64>(pads);
+		topology.ptr_interfaces = reinterpret_cast<__u64>(interfaces);
 
 		ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology);
 		if (ret < 0) {
@@ -236,10 +238,12 @@ int MediaDevice::populate()
 		delete[] links;
 		delete[] ents;
 		delete[] pads;
+		delete[] interfaces;
 
 		ents = new media_v2_entity[topology.num_entities];
 		links = new media_v2_link[topology.num_links];
 		pads = new media_v2_pad[topology.num_pads];
+		interfaces = new media_v2_interface[topology.num_interfaces];
 
 		version = topology.topology_version;
 	}
@@ -253,6 +257,7 @@ int MediaDevice::populate()
 	delete[] links;
 	delete[] ents;
 	delete[] pads;
+	delete[] interfaces;
 
 	if (!valid_) {
 		clear();
@@ -367,6 +372,45 @@ void MediaDevice::clear()
  * \brief Global list of media entities in the media graph
  */
 
+/**
+ * \brief Find the interface associated with an entity
+ * \param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY
+ * \param entityId The entity id
+ * \return A pointer to the interface if found, or nullptr otherwise
+ */
+struct media_v2_interface *MediaDevice::findInterface(const struct media_v2_topology &topology,
+			       unsigned int entityId)
+{
+	struct media_v2_link *links = reinterpret_cast<struct media_v2_link *>
+						(topology.ptr_links);
+	unsigned int ifaceId = -1;
+
+	for (unsigned int i = 0; i < topology.num_links; ++i) {
+		/* Search for the interface to entity link. */
+		if (links[i].sink_id != entityId)
+			continue;
+
+		if ((links[i].flags & MEDIA_LNK_FL_LINK_TYPE) !=
+		    MEDIA_LNK_FL_INTERFACE_LINK)
+			continue;
+
+		ifaceId = links[i].source_id;
+	}
+
+	if (ifaceId == static_cast<unsigned int>(-1))
+		return nullptr;
+
+	struct media_v2_interface *ifaces = reinterpret_cast<struct media_v2_interface *>
+						(topology.ptr_interfaces);
+
+	for (unsigned int i = 0; i < topology.num_interfaces; ++i) {
+		if (ifaces[i].id == ifaceId)
+			return &ifaces[i];
+	}
+
+	return nullptr;
+}
+
 /*
  * For each entity in the media graph create a MediaEntity and store a
  * reference in the media device objects map and entities list.
@@ -377,7 +421,26 @@ bool MediaDevice::populateEntities(const struct media_v2_topology &topology)
 					 (topology.ptr_entities);
 
 	for (unsigned int i = 0; i < topology.num_entities; ++i) {
-		MediaEntity *entity = new MediaEntity(&mediaEntities[i]);
+		/*
+		 * Find the interface linked to this entity to get the device
+		 * node major and minor numbers.
+		 */
+		struct media_v2_interface *iface =
+			findInterface(topology, mediaEntities[i].id);
+		if (!iface) {
+			LOG(Error) << "Failed to find interface link for "
+				   << "entity with id: " << mediaEntities[i].id;
+			return false;
+		}
+
+		MediaEntity *entity;
+		if (iface)
+			entity = new MediaEntity(&mediaEntities[i],
+						 iface->devnode.major,
+						 iface->devnode.minor);
+		else
+			entity = new MediaEntity(&mediaEntities[i]);
+
 		if (!addObject(entity)) {
 			delete entity;
 			return false;
diff --git a/src/libcamera/media_object.cpp b/src/libcamera/media_object.cpp
index b64dcc3c8fb4..69e5cc74264d 100644
--- a/src/libcamera/media_object.cpp
+++ b/src/libcamera/media_object.cpp
@@ -6,9 +6,8 @@
  */
 
 #include <errno.h>
-#include <fcntl.h>
 #include <string.h>
-#include <sys/stat.h>
+#include <unistd.h>
 
 #include <string>
 #include <vector>
@@ -212,6 +211,20 @@ void MediaPad::addLink(MediaLink *link)
  * \return The entity name
  */
 
+/**
+ * \fn MediaEntity::major()
+ * \brief Retrieve the major number of the interface associated with the entity
+ * \return The interface major number, or 0 if the entity isn't associated with
+ * an interface
+ */
+
+/**
+ * \fn MediaEntity::minor()
+ * \brief Retrieve the minor number of the interface associated with the entity
+ * \return The interface minor number, or 0 if the entity isn't associated with
+ * an interface
+ */
+
 /**
  * \fn MediaEntity::pads()
  * \brief Retrieve all pads of the entity
@@ -238,6 +251,7 @@ const MediaPad *MediaEntity::getPadByIndex(unsigned int index) const
  * \param id The pad id
  * \return The pad identified by \a id, or nullptr if no such pad exist
  */
+
 const MediaPad *MediaEntity::getPadById(unsigned int id) const
 {
 	for (MediaPad *p : pads_) {
@@ -248,12 +262,36 @@ const MediaPad *MediaEntity::getPadById(unsigned int id) const
 	return nullptr;
 }
 
+/**
+ * \brief Set the path to the device node for the associated interface
+ * \param devnode The interface device node path associated with this entity
+ * \return 0 on success, or a negative error code if the device node can't be
+ * accessed
+ */
+int MediaEntity::setDeviceNode(const std::string &devnode)
+{
+	/* Make sure the device node can be accessed. */
+	int ret = ::access(devnode.c_str(), R_OK | W_OK);
+	if (ret < 0) {
+		ret = -errno;
+		LOG(Error) << "Device node " << devnode << " can't be accessed: "
+			   << strerror(-ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 /**
  * \brief Construct a MediaEntity
  * \param entity The media entity kernel data
+ * \param major The major number of the entity associated interface
+ * \param minor The minor number of the entity associated interface
  */
-MediaEntity::MediaEntity(const struct media_v2_entity *entity)
-	: MediaObject(entity->id), name_(entity->name)
+MediaEntity::MediaEntity(const struct media_v2_entity *entity,
+			 unsigned int major, unsigned int minor)
+	: MediaObject(entity->id), name_(entity->name),
+	  major_(major), minor_(minor)
 {
 }
 
