[media] media: add functions to allow creating interfaces

Interfaces are different than entities: they represent a
Kernel<->userspace interaction, while entities represent a
piece of hardware/firmware/software that executes a function.

Let's distinguish them by creating a separate structure to
store the interfaces.

Later patches should change the existing drivers and logic
to split the current interface embedded inside the entity
structure (device nodes) into a separate object of the graph.

Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index 6ed4a19..160ce2c 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -44,11 +44,41 @@
 		return "pad";
 	case MEDIA_GRAPH_LINK:
 		return "link";
+	case MEDIA_GRAPH_INTF_DEVNODE:
+		return "intf-devnode";
 	default:
 		return "unknown";
 	}
 }
 
+static inline const char *intf_type(struct media_interface *intf)
+{
+	switch (intf->type) {
+	case MEDIA_INTF_T_DVB_FE:
+		return "frontend";
+	case MEDIA_INTF_T_DVB_DEMUX:
+		return "demux";
+	case MEDIA_INTF_T_DVB_DVR:
+		return "DVR";
+	case MEDIA_INTF_T_DVB_CA:
+		return  "CA";
+	case MEDIA_INTF_T_DVB_NET:
+		return "dvbnet";
+	case MEDIA_INTF_T_V4L_VIDEO:
+		return "video";
+	case MEDIA_INTF_T_V4L_VBI:
+		return "vbi";
+	case MEDIA_INTF_T_V4L_RADIO:
+		return "radio";
+	case MEDIA_INTF_T_V4L_SUBDEV:
+		return "v4l2-subdev";
+	case MEDIA_INTF_T_V4L_SWRADIO:
+		return "swradio";
+	default:
+		return "unknown-intf";
+	}
+};
+
 static void dev_dbg_obj(const char *event_name,  struct media_gobj *gobj)
 {
 #if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG)
@@ -84,6 +114,19 @@
 			"%s: id 0x%08x pad#%d: '%s':%d\n",
 			event_name, gobj->id, media_localid(gobj),
 			pad->entity->name, pad->index);
+		break;
+	}
+	case MEDIA_GRAPH_INTF_DEVNODE:
+	{
+		struct media_interface *intf = gobj_to_intf(gobj);
+		struct media_intf_devnode *devnode = intf_to_devnode(intf);
+
+		dev_dbg(gobj->mdev->dev,
+			"%s: id 0x%08x intf_devnode#%d: %s - major: %d, minor: %d\n",
+			event_name, gobj->id, media_localid(gobj),
+			intf_type(intf),
+			devnode->major, devnode->minor);
+		break;
 	}
 	}
 #endif
@@ -119,6 +162,9 @@
 	case MEDIA_GRAPH_LINK:
 		gobj->id = media_gobj_gen_id(type, ++mdev->link_id);
 		break;
+	case MEDIA_GRAPH_INTF_DEVNODE:
+		gobj->id = media_gobj_gen_id(type, ++mdev->intf_devnode_id);
+		break;
 	}
 	dev_dbg_obj(__func__, gobj);
 }
@@ -793,3 +839,40 @@
 
 }
 EXPORT_SYMBOL_GPL(media_entity_remote_pad);
+
+
+/* Functions related to the media interface via device nodes */
+
+struct media_intf_devnode *media_devnode_create(struct media_device *mdev,
+						u32 type, u32 flags,
+						u32 major, u32 minor,
+						gfp_t gfp_flags)
+{
+	struct media_intf_devnode *devnode;
+	struct media_interface *intf;
+
+	devnode = kzalloc(sizeof(*devnode), gfp_flags);
+	if (!devnode)
+		return NULL;
+
+	intf = &devnode->intf;
+
+	intf->type = type;
+	intf->flags = flags;
+
+	devnode->major = major;
+	devnode->minor = minor;
+
+	media_gobj_init(mdev, MEDIA_GRAPH_INTF_DEVNODE,
+		       &devnode->intf.graph_obj);
+
+	return devnode;
+}
+EXPORT_SYMBOL_GPL(media_devnode_create);
+
+void media_devnode_remove(struct media_intf_devnode *devnode)
+{
+	media_gobj_remove(&devnode->intf.graph_obj);
+	kfree(devnode);
+}
+EXPORT_SYMBOL_GPL(media_devnode_remove);