Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f50d5166 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab
Browse files

[media] dvbdev: returns error if graph object creation fails



Right now, if something gets wrong at dvb_create_media_entity()
or at dvb_create_media_graph(), the device will still be
registered.

Change the logic to properly handle it and free all media graph
objects if something goes wrong at dvb_register_device().

Also, change the logic at dvb_create_media_graph() to return
an error code if something goes wrong. It is up to the
caller to implement the right logic and to call
dvb_unregister_device() to unregister the already-created
objects.

While here, add a missing logic to unregister the created
interfaces.

Acked-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent eb83a517
Loading
Loading
Loading
Loading
+183 −130
Original line number Diff line number Diff line
@@ -183,7 +183,40 @@ static int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
	return -ENFILE;
}

static void dvb_create_tsout_entity(struct dvb_device *dvbdev,
static void dvb_media_device_free(struct dvb_device *dvbdev)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
	if (dvbdev->entity) {
		media_device_unregister_entity(dvbdev->entity);
		kfree(dvbdev->entity);
		kfree(dvbdev->pads);
		dvbdev->entity = NULL;
		dvbdev->pads = NULL;
	}

	if (dvbdev->tsout_entity) {
		int i;

		for (i = 0; i < dvbdev->tsout_num_entities; i++) {
			media_device_unregister_entity(&dvbdev->tsout_entity[i]);
			kfree(dvbdev->tsout_entity[i].name);
		}
		kfree(dvbdev->tsout_entity);
		kfree(dvbdev->tsout_pads);
		dvbdev->tsout_entity = NULL;
		dvbdev->tsout_pads = NULL;

		dvbdev->tsout_num_entities = 0;
	}

	if (dvbdev->intf_devnode) {
		media_devnode_remove(dvbdev->intf_devnode);
		dvbdev->intf_devnode = NULL;
	}
#endif
}

static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
				    const char *name, int npads)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
@@ -192,77 +225,62 @@ static void dvb_create_tsout_entity(struct dvb_device *dvbdev,
	dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads),
				     GFP_KERNEL);
	if (!dvbdev->tsout_pads)
		return;
		return -ENOMEM;

	dvbdev->tsout_entity = kcalloc(npads, sizeof(*dvbdev->tsout_entity),
				       GFP_KERNEL);
	if (!dvbdev->tsout_entity) {
		kfree(dvbdev->tsout_pads);
		dvbdev->tsout_pads = NULL;
		return;
	}
	if (!dvbdev->tsout_entity)
		return -ENOMEM;

	dvbdev->tsout_num_entities = npads;

	for (i = 0; i < npads; i++) {
		struct media_pad *pads = &dvbdev->tsout_pads[i];
		struct media_entity *entity = &dvbdev->tsout_entity[i];

		entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i);
		if (!entity->name) {
			ret = -ENOMEM;
			break;
		}
		if (!entity->name)
			return -ENOMEM;

		entity->type = MEDIA_ENT_T_DVB_TSOUT;
		pads->flags = MEDIA_PAD_FL_SINK;

		ret = media_entity_init(entity, 1, pads);
		if (ret < 0)
			break;
			return ret;

		ret = media_device_register_entity(dvbdev->adapter->mdev,
						   entity);
		if (ret < 0)
			break;
	}

	if (!ret) {
		dvbdev->tsout_num_entities = npads;
		return;
			return ret;
	}

	for (i--; i >= 0; i--) {
		media_device_unregister_entity(&dvbdev->tsout_entity[i]);
		kfree(dvbdev->tsout_entity[i].name);
	}

	printk(KERN_ERR
		"%s: media_device_register_entity failed for %s\n",
		__func__, name);

	kfree(dvbdev->tsout_entity);
	kfree(dvbdev->tsout_pads);
	dvbdev->tsout_entity = NULL;
	dvbdev->tsout_pads = NULL;
#endif
	return 0;
}

#define DEMUX_TSOUT	"demux-tsout"
#define DVR_TSOUT	"dvr-tsout"

static void dvb_create_media_entity(struct dvb_device *dvbdev,
static int dvb_create_media_entity(struct dvb_device *dvbdev,
				   int type, int demux_sink_pads)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
	int i, ret = 0, npads;
	int i, ret, npads;

	switch (type) {
	case DVB_DEVICE_FRONTEND:
		npads = 2;
		break;
	case DVB_DEVICE_DVR:
		dvb_create_tsout_entity(dvbdev, DVR_TSOUT, demux_sink_pads);
		return;
		ret = dvb_create_tsout_entity(dvbdev, DVR_TSOUT,
					      demux_sink_pads);
		return ret;
	case DVB_DEVICE_DEMUX:
		npads = 1 + demux_sink_pads;
		dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT, demux_sink_pads);
		ret = dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT,
					      demux_sink_pads);
		if (ret < 0)
			return ret;
		break;
	case DVB_DEVICE_CA:
		npads = 2;
@@ -277,24 +295,22 @@ static void dvb_create_media_entity(struct dvb_device *dvbdev,
		 * the Media Controller, let's not create the decap
		 * entities yet.
		 */
		return;
		return 0;
	default:
		return;
		return 0;
	}

	dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL);
	if (!dvbdev->entity)
		return;
		return -ENOMEM;

	dvbdev->entity->name = dvbdev->name;

	if (npads) {
		dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads),
				       GFP_KERNEL);
		if (!dvbdev->pads) {
			kfree(dvbdev->entity);
			return;
		}
		if (!dvbdev->pads)
			return -ENOMEM;
	}

	switch (type) {
@@ -315,50 +331,46 @@ static void dvb_create_media_entity(struct dvb_device *dvbdev,
		dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
		break;
	default:
		/* Should never happen, as the first switch prevents it */
		kfree(dvbdev->entity);
		kfree(dvbdev->pads);
		dvbdev->entity = NULL;
		return;
		dvbdev->pads = NULL;
		return 0;
	}

	if (npads)
	if (npads) {
		ret = media_entity_init(dvbdev->entity, npads, dvbdev->pads);
	if (!ret)
		if (ret)
			return ret;
	}
	ret = media_device_register_entity(dvbdev->adapter->mdev,
					   dvbdev->entity);
	if (ret < 0) {
		printk(KERN_ERR
			"%s: media_device_register_entity failed for %s\n",
			__func__, dvbdev->entity->name);

		media_device_unregister_entity(dvbdev->entity);
		for (i = 0; i < dvbdev->tsout_num_entities; i++) {
			media_device_unregister_entity(&dvbdev->tsout_entity[i]);
			kfree(dvbdev->tsout_entity[i].name);
		}
		kfree(dvbdev->pads);
		kfree(dvbdev->entity);
		kfree(dvbdev->tsout_pads);
		kfree(dvbdev->tsout_entity);
		dvbdev->entity = NULL;
		return;
	}
	if (ret)
		return (ret);

	printk(KERN_DEBUG "%s: media entity '%s' registered.\n",
		__func__, dvbdev->entity->name);

#endif
	return 0;
}

static void dvb_register_media_device(struct dvb_device *dvbdev,
static int dvb_register_media_device(struct dvb_device *dvbdev,
				     int type, int minor,
				     unsigned demux_sink_pads)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
	struct media_link *link;
	u32 intf_type;
	int ret;

	if (!dvbdev->adapter->mdev)
		return;
		return 0;

	dvb_create_media_entity(dvbdev, type, demux_sink_pads);
	ret = dvb_create_media_entity(dvbdev, type, demux_sink_pads);
	if (ret)
		return ret;

	switch (type) {
	case DVB_DEVICE_FRONTEND:
@@ -377,7 +389,7 @@ static void dvb_register_media_device(struct dvb_device *dvbdev,
		intf_type = MEDIA_INTF_T_DVB_NET;
		break;
	default:
		return;
		return 0;
	}

	dvbdev->intf_devnode = media_devnode_create(dvbdev->adapter->mdev,
@@ -385,6 +397,9 @@ static void dvb_register_media_device(struct dvb_device *dvbdev,
						    DVB_MAJOR, minor,
						    GFP_KERNEL);

	if (!dvbdev->intf_devnode)
		return -ENOMEM;

	/*
	 * Create the "obvious" link, e. g. the ones that represent
	 * a direct association between an interface and an entity.
@@ -393,13 +408,15 @@ static void dvb_register_media_device(struct dvb_device *dvbdev,
	 *		DVB demux intf -> dvr
	 */

	if (!dvbdev->entity || !dvbdev->intf_devnode)
		return;
	if (!dvbdev->entity)
		return 0;

	media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf,
	link = media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf,
				      MEDIA_LNK_FL_ENABLED);

	if (!link)
		return -ENOMEM;
#endif
	return 0;
}

int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
@@ -410,7 +427,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
	struct file_operations *dvbdevfops;
	struct device *clsdev;
	int minor;
	int id;
	int id, ret;

	mutex_lock(&dvbdev_register_lock);

@@ -421,7 +438,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
		return -ENFILE;
	}

	*pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
	*pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL);

	if (!dvbdev){
		mutex_unlock(&dvbdev_register_lock);
@@ -470,6 +487,20 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
	dvb_minors[minor] = dvbdev;
	up_write(&minor_rwsem);

	ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads);
	if (ret) {
		printk(KERN_ERR
		      "%s: dvb_register_media_device failed to create the mediagraph\n",
		      __func__);

		dvb_media_device_free(dvbdev);
		kfree(dvbdevfops);
		kfree(dvbdev);
		up_write(&minor_rwsem);
		mutex_unlock(&dvbdev_register_lock);
		return ret;
	}

	mutex_unlock(&dvbdev_register_lock);

	clsdev = device_create(dvb_class, adap->device,
@@ -483,8 +514,6 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
	dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
		adap->num, dnames[type], id, minor, minor);

	dvb_register_media_device(dvbdev, type, minor, demux_sink_pads);

	return 0;
}
EXPORT_SYMBOL(dvb_register_device);
@@ -499,24 +528,9 @@ void dvb_unregister_device(struct dvb_device *dvbdev)
	dvb_minors[dvbdev->minor] = NULL;
	up_write(&minor_rwsem);

	device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
	dvb_media_device_free(dvbdev);

#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
	if (dvbdev->entity) {
		int i;

		media_device_unregister_entity(dvbdev->entity);
		for (i = 0; i < dvbdev->tsout_num_entities; i++) {
			media_device_unregister_entity(&dvbdev->tsout_entity[i]);
			kfree(dvbdev->tsout_entity[i].name);
		}

		kfree(dvbdev->entity);
		kfree(dvbdev->pads);
		kfree(dvbdev->tsout_entity);
		kfree(dvbdev->tsout_pads);
	}
#endif
	device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));

	list_del (&dvbdev->list_head);
	kfree (dvbdev->fops);
@@ -526,17 +540,19 @@ EXPORT_SYMBOL(dvb_unregister_device);


#ifdef CONFIG_MEDIA_CONTROLLER_DVB
void dvb_create_media_graph(struct dvb_adapter *adap)
int dvb_create_media_graph(struct dvb_adapter *adap)
{
	struct media_device *mdev = adap->mdev;
	struct media_entity *entity, *tuner = NULL, *demod = NULL;
	struct media_entity *demux = NULL, *ca = NULL;
	struct media_link *link;
	struct media_interface *intf;
	unsigned demux_pad = 0;
	unsigned dvr_pad = 0;
	int ret;

	if (!mdev)
		return;
		return 0;

	media_device_for_each_entity(entity, mdev) {
		switch (entity->type) {
@@ -555,57 +571,94 @@ void dvb_create_media_graph(struct dvb_adapter *adap)
		}
	}

	if (tuner && demod)
		media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, demod, 0, 0);
	if (tuner && demod) {
		ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT,
					    demod, 0, MEDIA_LNK_FL_ENABLED);
		if (ret)
			return ret;
	}

	if (demod && demux)
		media_create_pad_link(demod, 1, demux, 0, MEDIA_LNK_FL_ENABLED);
	if (demux && ca)
		media_create_pad_link(demux, 1, ca, 0, MEDIA_LNK_FL_ENABLED);
	if (demod && demux) {
		ret = media_create_pad_link(demod, 1, demux,
					    0, MEDIA_LNK_FL_ENABLED);
		if (ret)
			return -ENOMEM;
	}
	if (demux && ca) {
		ret = media_create_pad_link(demux, 1, ca,
					    0, MEDIA_LNK_FL_ENABLED);
		if (!ret)
			return -ENOMEM;
	}

	/* Create demux links for each ringbuffer/pad */
	if (demux) {
		media_device_for_each_entity(entity, mdev) {
			if (entity->type == MEDIA_ENT_T_DVB_TSOUT) {
				if (!strncmp(entity->name, DVR_TSOUT,
					strlen(DVR_TSOUT)))
					media_create_pad_link(demux,
				    strlen(DVR_TSOUT))) {
					ret = media_create_pad_link(demux,
								++dvr_pad,
							    entity, 0, 0);
					if (ret)
						return ret;
				}
				if (!strncmp(entity->name, DEMUX_TSOUT,
					strlen(DEMUX_TSOUT)))
					media_create_pad_link(demux,
				    strlen(DEMUX_TSOUT))) {
					ret = media_create_pad_link(demux,
							      ++demux_pad,
							    entity, 0, 0);
					if (ret)
						return ret;
				}
			}
		}
	}

	/* Create indirect interface links for FE->tuner, DVR->demux and CA->ca */
	media_device_for_each_intf(intf, mdev) {
		if (intf->type == MEDIA_INTF_T_DVB_CA && ca)
			media_create_intf_link(ca, intf, MEDIA_LNK_FL_ENABLED);
		if (intf->type == MEDIA_INTF_T_DVB_CA && ca) {
			link = media_create_intf_link(ca, intf,
						      MEDIA_LNK_FL_ENABLED);
			if (!link)
				return -ENOMEM;
		}

		if (intf->type == MEDIA_INTF_T_DVB_FE && tuner)
			media_create_intf_link(tuner, intf,
		if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) {
			link = media_create_intf_link(tuner, intf,
						      MEDIA_LNK_FL_ENABLED);
			if (!link)
				return -ENOMEM;
		}

		if (intf->type == MEDIA_INTF_T_DVB_DVR && demux)
			media_create_intf_link(demux, intf,
		if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) {
			link = media_create_intf_link(demux, intf,
						      MEDIA_LNK_FL_ENABLED);
			if (!link)
				return -ENOMEM;
		}

		media_device_for_each_entity(entity, mdev) {
			if (entity->type == MEDIA_ENT_T_DVB_TSOUT) {
				if (!strcmp(entity->name, DVR_TSOUT))
					media_create_intf_link(entity, intf,
				if (!strcmp(entity->name, DVR_TSOUT)) {
					link = media_create_intf_link(entity,
							intf,
							MEDIA_LNK_FL_ENABLED);
				if (!strcmp(entity->name, DEMUX_TSOUT))
					media_create_intf_link(entity, intf,
					if (!link)
						return -ENOMEM;
				}
				if (!strcmp(entity->name, DEMUX_TSOUT)) {
					link = media_create_intf_link(entity,
							intf,
							MEDIA_LNK_FL_ENABLED);
					if (!link)
						return -ENOMEM;
				}
				break;
			}
		}
	}
	return 0;
}
EXPORT_SYMBOL_GPL(dvb_create_media_graph);
#endif
+5 −2
Original line number Diff line number Diff line
@@ -206,7 +206,7 @@ int dvb_register_device(struct dvb_adapter *adap,
void dvb_unregister_device(struct dvb_device *dvbdev);

#ifdef CONFIG_MEDIA_CONTROLLER_DVB
void dvb_create_media_graph(struct dvb_adapter *adap);
int dvb_create_media_graph(struct dvb_adapter *adap);
static inline void dvb_register_media_controller(struct dvb_adapter *adap,
						 struct media_device *mdev)
{
@@ -214,7 +214,10 @@ static inline void dvb_register_media_controller(struct dvb_adapter *adap,
}

#else
static inline void dvb_create_media_graph(struct dvb_adapter *adap) {}
static inline int dvb_create_media_graph(struct dvb_adapter *adap)
{
	return 0;
};
#define dvb_register_media_controller(a, b) {}
#endif

+5 −5
Original line number Diff line number Diff line
@@ -576,6 +576,10 @@ void media_device_unregister(struct media_device *mdev)
	struct media_entity *next;
	struct media_interface *intf, *tmp_intf;

	/* Remove all entities from the media device */
	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
		media_device_unregister_entity(entity);

	/* Remove all interfaces from the media device */
	spin_lock(&mdev->lock);
	list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
@@ -586,9 +590,6 @@ void media_device_unregister(struct media_device *mdev)
	}
	spin_unlock(&mdev->lock);

	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
		media_device_unregister_entity(entity);

	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
	media_devnode_unregister(&mdev->devnode);

@@ -654,8 +655,7 @@ void media_device_unregister_entity(struct media_entity *entity)
	/* Remove all interface links pointing to this entity */
	list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) {
		list_for_each_entry_safe(link, tmp, &intf->links, list) {
			if (media_type(link->gobj1) == MEDIA_GRAPH_ENTITY
			    && link->entity == entity)
			if (link->entity == entity)
				__media_remove_intf_link(link);
		}
	}