Commit 52dee392 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab
Browse files

[media] dvb_frontend: Simplify the emulation logic



The current logic was broken and too complex; while it works
fine for DVB-S2/DVB-S, it is broken for ISDB-T.
Make the logic simpler, fixes it for ISDB-T and make it clearer.

Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent be431b16
Loading
Loading
Loading
Loading
+109 −110
Original line number Diff line number Diff line
@@ -1509,9 +1509,17 @@ static bool is_dvbv3_delsys(u32 delsys)
	return status;
}

static int emulate_delivery_system(struct dvb_frontend *fe,
				   enum dvbv3_emulation_type type,
				   u32 delsys, u32 desired_system)
/**
 * emulate_delivery_system - emulate a DVBv5 delivery system with a DVBv3 type
 * @fe:			struct frontend;
 * @delsys:			DVBv5 type that will be used for emulation
 *
 * Provides emulation for delivery systems that are compatible with the old
 * DVBv3 call. Among its usages, it provices support for ISDB-T, and allows
 * using a DVB-S2 only frontend just like it were a DVB-S, if the frontent
 * parameters are compatible with DVB-S spec.
 */
static int emulate_delivery_system(struct dvb_frontend *fe, u32 delsys)
{
	int i;
	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
@@ -1519,23 +1527,8 @@ static int emulate_delivery_system(struct dvb_frontend *fe,
	c->delivery_system = delsys;

	/*
	 * The DVBv3 or DVBv5 call is requesting a different system. So,
	 * emulation is needed.
	 *
	 * Emulate newer delivery systems like ISDBT, DVBT and DTMB
	 * for older DVBv5 applications. The emulation will try to use
	 * the auto mode for most things, and will assume that the desired
	 * delivery system is the last one at the ops.delsys[] array
	 */
	dev_dbg(fe->dvb->device,
		"%s: Using delivery system %d emulated as if it were a %d\n",
		__func__, delsys, desired_system);

	/*
	 * For now, handles ISDB-T calls. More code may be needed here for the
	 * other emulated stuff
	 * If the call is for ISDB-T, put it into full-seg, auto mode, TV
	 */
	if (type == DVBV3_OFDM) {
	if (c->delivery_system == SYS_ISDBT) {
		dev_dbg(fe->dvb->device,
			"%s: Using defaults for SYS_ISDBT\n",
@@ -1549,7 +1542,7 @@ static int emulate_delivery_system(struct dvb_frontend *fe,
		c->isdbt_sb_subchannel = 0;
		c->isdbt_sb_segment_idx = 0;
		c->isdbt_sb_segment_count = 0;
			c->isdbt_layer_enabled = 0;
		c->isdbt_layer_enabled = 7;
		for (i = 0; i < 3; i++) {
			c->layer[i].fec = FEC_AUTO;
			c->layer[i].modulation = QAM_AUTO;
@@ -1557,13 +1550,29 @@ static int emulate_delivery_system(struct dvb_frontend *fe,
			c->layer[i].segment_count = 0;
		}
	}
	}
	dev_dbg(fe->dvb->device, "%s: change delivery system on cache to %d\n",
		__func__, c->delivery_system);

	return 0;
}

/**
 * dvbv5_set_delivery_system - Sets the delivery system for a DVBv5 API call
 * @fe:			frontend struct
 * @desired_system:	delivery system requested by the user
 *
 * A DVBv5 call know what's the desired system it wants. So, set it.
 *
 * There are, however, a few known issues with early DVBv5 applications that
 * are also handled by this logic:
 *
 * 1) Some early apps use SYS_UNDEFINED as the desired delivery system.
 *    This is an API violation, but, as we don't want to break userspace,
 *    convert it to the first supported delivery system.
 * 2) Some apps might be using a DVBv5 call in a wrong way, passing, for
 *    example, SYS_DVBT instead of SYS_ISDBT. This is because early usage of
 *    ISDB-T provided backward compat with DVB-T.
 */
static int dvbv5_set_delivery_system(struct dvb_frontend *fe,
				     u32 desired_system)
{
@@ -1578,15 +1587,14 @@ static int dvbv5_set_delivery_system(struct dvb_frontend *fe,
	 * assume that the application wants to use the first supported
	 * delivery system.
	 */
	if (c->delivery_system == SYS_UNDEFINED)
		c->delivery_system = fe->ops.delsys[0];
	if (desired_system == SYS_UNDEFINED)
		desired_system = fe->ops.delsys[0];

	/*
	 * This is a DVBv5 call. So, it likely knows the supported
	* delivery systems.
	 * delivery systems. So, check if the desired delivery system is
	 * supported
	 */

	/* Check if the desired delivery system is supported */
	ncaps = 0;
	while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
		if (fe->ops.delsys[ncaps] == desired_system) {
@@ -1600,69 +1608,85 @@ static int dvbv5_set_delivery_system(struct dvb_frontend *fe,
	}

	/*
	 * Need to emulate a delivery system
	 */

	type = dvbv3_type(desired_system);

	/*
	* The delivery system is not supported. See if it can be
	* emulated.
	 * The requested delivery system isn't supported. Maybe userspace
	 * is requesting a DVBv3 compatible delivery system.
	 *
	 * The emulation only works if the desired system is one of the
	* DVBv3 delivery systems
	 * delivery systems supported by DVBv3 API
	 */
	if (!is_dvbv3_delsys(desired_system)) {
		dev_dbg(fe->dvb->device,
			"%s: can't use a DVBv3 FE_SET_FRONTEND call for this frontend\n",
			__func__);
			"%s: Delivery system %d not supported.\n",
			__func__, desired_system);
		return -EINVAL;
	}

	type = dvbv3_type(desired_system);

	/*
	* Get the last non-DVBv3 delivery system that has the same type
	* of the desired system
	*/
	ncaps = 0;
	while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
		if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) &&
		    !is_dvbv3_delsys(fe->ops.delsys[ncaps]))
		if (dvbv3_type(fe->ops.delsys[ncaps]) == type)
			delsys = fe->ops.delsys[ncaps];
		ncaps++;
	}

	/* There's nothing compatible with the desired delivery system */
	if (delsys == SYS_UNDEFINED) {
		dev_dbg(fe->dvb->device,
				"%s: Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n",
				__func__);
			"%s: Delivery system %d not supported on emulation mode.\n",
			__func__, desired_system);
		return -EINVAL;
	}

	return emulate_delivery_system(fe, type, delsys, desired_system);
	dev_dbg(fe->dvb->device,
		"%s: Using delivery system %d emulated as if it were %d\n",
		__func__, delsys, desired_system);

	return emulate_delivery_system(fe, desired_system);
}

/**
 * dvbv3_set_delivery_system - Sets the delivery system for a DVBv3 API call
 * @fe:	frontend struct
 *
 * A DVBv3 call doesn't know what's the desired system it wants. It also
 * doesn't allow to switch between different types. Due to that, userspace
 * should use DVBv5 instead.
 * However, in order to avoid breaking userspace API, limited backward
 * compatibility support is provided.
 *
 * There are some delivery systems that are incompatible with DVBv3 calls.
 *
 * This routine should work fine for frontends that support just one delivery
 * system.
 *
 * For frontends that support multiple frontends:
 * 1) It defaults to use the first supported delivery system. There's an
 *    userspace application that allows changing it at runtime;
 *
 * 2) If the current delivery system is not compatible with DVBv3, it gets
 *    the first one that it is compatible.
 *
 * NOTE: in order for this to work with applications like Kaffeine that
 *	uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to
 *	DVB-S, drivers that support both DVB-S and DVB-S2 should have the
 *	SYS_DVBS entry before the SYS_DVBS2, otherwise it won't switch back
 *	to DVB-S.
 */
static int dvbv3_set_delivery_system(struct dvb_frontend *fe)
{
	int ncaps;
	u32 desired_system;
	u32 delsys = SYS_UNDEFINED;
	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
	enum dvbv3_emulation_type type;

	/* If not set yet, defaults to the first supported delivery system */
	if (c->delivery_system == SYS_UNDEFINED)
		c->delivery_system = fe->ops.delsys[0];

	/*
	 * A DVBv3 call doesn't know what's the desired system.
	 * Also, DVBv3 applications don't know that ops.info->type
	 * could be changed, and they simply don't tune when it doesn't
	 * match.
	 * So, don't change the current delivery system, as it
	 * may be trying to do the wrong thing, like setting an
	 * ISDB-T frontend as DVB-T. Instead, find the closest
	 * DVBv3 system that matches the delivery system.
	 */

	/*
	 * Trivial case: just use the current one, if it already a DVBv3
	 * delivery system
@@ -1674,50 +1698,25 @@ static int dvbv3_set_delivery_system(struct dvb_frontend *fe)
		return 0;
	}

	/* Convert from DVBv3 into DVBv5 namespace */
	type = dvbv3_type(c->delivery_system);
	switch (type) {
	case DVBV3_QPSK:
		desired_system = SYS_DVBS;
		break;
	case DVBV3_QAM:
		desired_system = SYS_DVBC_ANNEX_A;
		break;
	case DVBV3_ATSC:
		desired_system = SYS_ATSC;
		break;
	case DVBV3_OFDM:
		desired_system = SYS_DVBT;
		break;
	default:
		dev_dbg(fe->dvb->device, "%s: This frontend doesn't support DVBv3 calls\n",
				__func__);
		return -EINVAL;
	}

	/*
	 * Get a delivery system that is compatible with DVBv3
	 * NOTE: in order for this to work with softwares like Kaffeine that
	 *	uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to
	 *	DVB-S, drivers that support both should put the SYS_DVBS entry
	 *	before the SYS_DVBS2, otherwise it won't switch back to DVB-S.
	 *	The real fix is that userspace applications should not use DVBv3
	 *	and not trust on calling FE_SET_FRONTEND to switch the delivery
	 *	system.
	 * Seek for the first delivery system that it is compatible with a
	 * DVBv3 standard
	 */
	ncaps = 0;
	while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
		if (fe->ops.delsys[ncaps] == desired_system) {
			delsys = desired_system;
		if (dvbv3_type(fe->ops.delsys[ncaps]) != DVBV3_UNKNOWN) {
			delsys = fe->ops.delsys[ncaps];
			break;
		}
		ncaps++;
	}
	if (delsys == SYS_UNDEFINED) {
		dev_dbg(fe->dvb->device, "%s: Couldn't find a delivery system that matches %d\n",
			__func__, desired_system);
		dev_dbg(fe->dvb->device,
			"%s: Couldn't find a delivery system that works with FE_SET_FRONTEND\n",
			__func__);
		return -EINVAL;
	}
	return emulate_delivery_system(fe, type, delsys, desired_system);
	return emulate_delivery_system(fe, delsys);
}

static int dtv_property_process_set(struct dvb_frontend *fe,