diff mbox

Details about DVB frontend API

Message ID 4B1D6CFA.2020602@infradead.org (mailing list archive)
State RFC
Headers show

Commit Message

Mauro Carvalho Chehab Dec. 7, 2009, 9 p.m. UTC
None
diff mbox

Patch

diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
--- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -975,6 +975,16 @@  static struct dtv_cmds_h dtv_cmds[] = {
 	_DTV_CMD(DTV_GUARD_INTERVAL, 0, 0),
 	_DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0),
 	_DTV_CMD(DTV_HIERARCHY, 0, 0),
+
+	/* Statistics API */
+	_DTV_CMD(DTV_FE_QUALITY, 0, 0),
+	_DTV_CMD(DTV_FE_QUALITY_UNIT, 0, 0),
+	_DTV_CMD(DTV_FE_STRENGTH, 0, 0),
+	_DTV_CMD(DTV_FE_STRENGTH_UNIT, 0, 0),
+	_DTV_CMD(DTV_FE_ERROR, 0, 0),
+	_DTV_CMD(DTV_FE_ERROR_UNIT, 0, 0),
+	_DTV_CMD(DTV_FE_SIGNAL, 0, 0),
+	_DTV_CMD(DTV_FE_SIGNAL_UNIT, 0, 0),
 };
 
 static void dtv_property_dump(struct dtv_property *tvp)
@@ -1203,16 +1213,59 @@  static int dvb_frontend_ioctl_legacy(str
 static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
 			unsigned int cmd, void *parg);
 
+static int dtv_property_prepare_get_stats(struct dvb_frontend *fe,
+				    struct dtv_property *tvp,
+				    struct inode *inode, struct file *file)
+{
+	switch (tvp->cmd) {
+	case DTV_FE_QUALITY:
+		fe->dtv_property_cache.need_stats |= FE_NEED_QUALITY;
+		break;
+	case DTV_FE_QUALITY_UNIT:
+		fe->dtv_property_cache.need_stats |= FE_NEED_QUALITY_UNIT;
+		break;
+	case DTV_FE_STRENGTH:
+		fe->dtv_property_cache.need_stats |= FE_NEED_STRENGTH;
+		break;
+	case DTV_FE_STRENGTH_UNIT:
+		fe->dtv_property_cache.need_stats |= FE_NEED_STRENGTH_UNIT;
+		break;
+	case DTV_FE_ERROR:
+		fe->dtv_property_cache.need_stats |= FE_NEED_ERROR;
+		break;
+	case DTV_FE_ERROR_UNIT:
+		fe->dtv_property_cache.need_stats |= FE_NEED_ERROR_UNIT;
+		break;
+	case DTV_FE_SIGNAL:
+		fe->dtv_property_cache.need_stats |= FE_NEED_SIGNAL;
+		break;
+	case DTV_FE_SIGNAL_UNIT:
+		fe->dtv_property_cache.need_stats |= FE_NEED_SIGNAL_UNIT;
+		break;
+	case DTV_FE_UNC:
+		fe->dtv_property_cache.need_stats |= FE_NEED_SIGNAL;
+		break;
+	case DTV_FE_UNC_UNIT:
+		fe->dtv_property_cache.need_stats |= FE_NEED_SIGNAL_UNIT;
+		break;
+	default:
+		return 1;
+	};
+
+	return 0;
+}
+
 static int dtv_property_process_get(struct dvb_frontend *fe,
 				    struct dtv_property *tvp,
-				    struct inode *inode, struct file *file)
+				    struct inode *inode, struct file *file,
+				    int need_get_ops)
 {
 	int r = 0;
 
 	dtv_property_dump(tvp);
 
 	/* Allow the frontend to validate incoming properties */
-	if (fe->ops.get_property)
+	if (fe->ops.get_property && need_get_ops)
 		r = fe->ops.get_property(fe, tvp);
 
 	if (r < 0)
@@ -1329,6 +1382,38 @@  static int dtv_property_process_get(stru
 	case DTV_ISDBS_TS_ID:
 		tvp->u.data = fe->dtv_property_cache.isdbs_ts_id;
 		break;
+
+	/* Quality measures */
+	case DTV_FE_QUALITY:
+		tvp->u.data = fe->dtv_property_cache.quality;
+		break;
+	case DTV_FE_QUALITY_UNIT:
+		tvp->u.data = fe->dtv_property_cache.quality_unit;
+		break;
+	case DTV_FE_STRENGTH:
+		tvp->u.data = fe->dtv_property_cache.strength;
+		break;
+	case DTV_FE_STRENGTH_UNIT:
+		tvp->u.data = fe->dtv_property_cache.strength_unit;
+		break;
+	case DTV_FE_ERROR:
+		tvp->u.data = fe->dtv_property_cache.error;
+		break;
+	case DTV_FE_ERROR_UNIT:
+		tvp->u.data = fe->dtv_property_cache.error_unit;
+		break;
+	case DTV_FE_SIGNAL:
+		tvp->u.data = fe->dtv_property_cache.signal;
+		break;
+	case DTV_FE_SIGNAL_UNIT:
+		tvp->u.data = fe->dtv_property_cache.signal_unit;
+		break;
+	case DTV_FE_UNC:
+		tvp->u.data = fe->dtv_property_cache.signal;
+		break;
+	case DTV_FE_UNC_UNIT:
+		tvp->u.data = fe->dtv_property_cache.signal_unit;
+		break;
 	default:
 		r = -1;
 	}
@@ -1527,7 +1612,7 @@  static int dvb_frontend_ioctl_properties
 {
 	struct dvb_device *dvbdev = file->private_data;
 	struct dvb_frontend *fe = dvbdev->priv;
-	int err = 0;
+	int err = 0, need_get_ops;
 
 	struct dtv_properties *tvps = NULL;
 	struct dtv_property *tvp = NULL;
@@ -1591,8 +1676,29 @@  static int dvb_frontend_ioctl_properties
 			goto out;
 		}
 
+		/*
+		* Do all get operations at once, instead of handling them
+		* individually
+		*/
+		need_get_ops = 0;
+		fe->dtv_property_cache.need_stats = 0;
+		for (i = 0; i < tvps->num; i++)
+			need_get_ops += dtv_property_prepare_get_stats(fe,
+							 tvp + i, inode, file);
+
+		if (!fe->dtv_property_cache.need_stats) {
+			need_get_ops++;
+		} else {
+			if (fe->ops.get_stats) {
+				err = fe->ops.get_stats(fe);
+				if (err < 0)
+					return err;
+			}
+		}
+
 		for (i = 0; i < tvps->num; i++) {
-			(tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file);
+			(tvp + i)->result = dtv_property_process_get(fe,
+					tvp + i, inode, file, need_get_ops);
 			err |= (tvp + i)->result;
 		}
 
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
--- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -304,6 +304,7 @@  struct dvb_frontend_ops {
 
 	int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
 	int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+	int (*get_stats)(struct dvb_frontend* fe);
 };
 
 #define MAX_EVENT 8
@@ -358,6 +359,32 @@  struct dtv_frontend_properties {
 
 	/* ISDB-T specifics */
 	u32			isdbs_ts_id;
+
+	/* Statistics group */
+
+#define FE_NEED_QUALITY		(1 << 0)
+#define FE_NEED_QUALITY_UNIT	(1 << 1)
+#define FE_NEED_STRENGTH	(1 << 2)
+#define FE_NEED_STRENGTH_UNIT	(1 << 3)
+#define FE_NEED_ERROR		(1 << 4)
+#define FE_NEED_ERROR_UNIT	(1 << 5)
+#define FE_NEED_UNC		(1 << 6)
+#define FE_NEED_UNC_UNIT	(1 << 7)
+#define FE_NEED_SIGNAL		(1 << 6)
+#define FE_NEED_SIGNAL_UNIT	(1 << 7)
+	int			need_stats;
+
+	u32			quality;
+	u32			strength;
+	u32			error;
+	u32			unc;
+	u32			signal;
+
+	enum fecap_quality_params	quality_unit;
+	enum fecap_scale_params		strength_unit;
+	enum fecap_error_params		error_unit;
+	enum fecap_unc_params		unc_unit;
+	enum fecap_scale_params		signal_unit;
 };
 
 struct dvb_frontend {
diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c
--- a/linux/drivers/media/dvb/frontends/cx24123.c
+++ b/linux/drivers/media/dvb/frontends/cx24123.c
@@ -890,6 +890,66 @@  static int cx24123_read_status(struct dv
 	return 0;
 }
 
+static int cx24123_get_stats(struct dvb_frontend* fe)
+{
+	struct cx24123_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *prop = &fe->dtv_property_cache;
+
+	if (fe->dtv_property_cache.need_stats & FE_NEED_STRENGTH) {
+		/* larger = better */
+		prop->strength = cx24123_readreg(state, 0x3b) << 8;
+			dprintk("Signal strength = %d\n", prop->strength);
+		fe->dtv_property_cache.need_stats &= ~FE_NEED_STRENGTH;
+	}
+
+	if (fe->dtv_property_cache.need_stats & FE_NEED_STRENGTH_UNIT) {
+		/* larger = better */
+		prop->strength_unit = FE_SCALE_UNKNOWN;
+		fe->dtv_property_cache.need_stats &= ~FE_NEED_STRENGTH_UNIT;
+	}
+
+	if (fe->dtv_property_cache.need_stats & FE_NEED_ERROR) {
+		/* The true bit error rate is this value divided by
+		the window size (set as 256 * 255) */
+		prop->error = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
+			       (cx24123_readreg(state, 0x1d) << 8 |
+			       cx24123_readreg(state, 0x1e));
+
+		dprintk("BER = %d\n", prop->error);
+
+		fe->dtv_property_cache.need_stats &= ~FE_NEED_ERROR;
+	}
+
+	if (fe->dtv_property_cache.need_stats & FE_NEED_ERROR_UNIT) {
+		/* larger = better */
+		prop->strength_unit = FE_ERROR_BER;
+		fe->dtv_property_cache.need_stats &= ~FE_NEED_ERROR_UNIT;
+	}
+
+	if (fe->dtv_property_cache.need_stats & FE_NEED_QUALITY) {
+		/* Inverted raw Es/N0 count, totally bogus but better than the
+		   BER threshold. */
+		prop->quality = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) |
+					  (u16)cx24123_readreg(state, 0x19));
+
+		dprintk("read S/N index = %d\n", prop->quality);
+
+		fe->dtv_property_cache.need_stats &= ~FE_NEED_QUALITY;
+	}
+
+	if (fe->dtv_property_cache.need_stats & FE_NEED_QUALITY_UNIT) {
+		/* larger = better */
+		prop->strength_unit = FE_QUALITY_EsNo;
+		fe->dtv_property_cache.need_stats &= ~FE_NEED_QUALITY_UNIT;
+	}
+
+	/* Check if userspace requested a parameter that we can't handle*/
+	if (fe->dtv_property_cache.need_stats)
+		return -EINVAL;
+
+	return 0;
+}
+
 /*
  * Configured to return the measurement of errors in blocks,
  * because no UCBLOCKS value is available, so this value doubles up
@@ -897,43 +957,30 @@  static int cx24123_read_status(struct dv
  */
 static int cx24123_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
-	struct cx24123_state *state = fe->demodulator_priv;
+	fe->dtv_property_cache.need_stats = FE_NEED_ERROR;
+	cx24123_get_stats(fe);
 
-	/* The true bit error rate is this value divided by
-	   the window size (set as 256 * 255) */
-	*ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
-		(cx24123_readreg(state, 0x1d) << 8 |
-		 cx24123_readreg(state, 0x1e));
-
-	dprintk("BER = %d\n", *ber);
-
+	*ber = fe->dtv_property_cache.error;
 	return 0;
 }
 
 static int cx24123_read_signal_strength(struct dvb_frontend *fe,
 	u16 *signal_strength)
 {
-	struct cx24123_state *state = fe->demodulator_priv;
-
-	/* larger = better */
-	*signal_strength = cx24123_readreg(state, 0x3b) << 8;
-
-	dprintk("Signal strength = %d\n", *signal_strength);
-
+	fe->dtv_property_cache.need_stats = FE_NEED_STRENGTH;
+	cx24123_get_stats(fe);
+	*signal_strength = fe->dtv_property_cache.strength;
 	return 0;
 }
 
+
 static int cx24123_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
-	struct cx24123_state *state = fe->demodulator_priv;
-
 	/* Inverted raw Es/N0 count, totally bogus but better than the
-	   BER threshold. */
-	*snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) |
-			 (u16)cx24123_readreg(state, 0x19));
-
-	dprintk("read S/N index = %d\n", *snr);
-
+		BER threshold. */
+	fe->dtv_property_cache.need_stats = FE_NEED_QUALITY;
+	cx24123_get_stats(fe);
+	*snr = fe->dtv_property_cache.quality;
 	return 0;
 }
 
@@ -1174,6 +1221,7 @@  static struct dvb_frontend_ops cx24123_o
 	.set_voltage = cx24123_set_voltage,
 	.tune = cx24123_tune,
 	.get_frontend_algo = cx24123_get_algo,
+	.get_stats = cx24123_get_stats,
 };
 
 MODULE_DESCRIPTION("DVB Frontend module for Conexant " \
diff --git a/linux/include/linux/dvb/frontend.h b/linux/include/linux/dvb/frontend.h
--- a/linux/include/linux/dvb/frontend.h
+++ b/linux/include/linux/dvb/frontend.h
@@ -304,7 +304,19 @@  struct dvb_frontend_event {
 
 #define DTV_ISDBS_TS_ID		42
 
-#define DTV_MAX_COMMAND				DTV_ISDBS_TS_ID
+/* Quality parameters */
+#define DTV_FE_QUALITY		43
+#define DTV_FE_QUALITY_UNIT	44
+#define DTV_FE_STRENGTH		45
+#define DTV_FE_STRENGTH_UNIT	46
+#define DTV_FE_ERROR		47
+#define DTV_FE_ERROR_UNIT	48
+#define DTV_FE_SIGNAL		49
+#define DTV_FE_SIGNAL_UNIT	50
+#define DTV_FE_UNC		51
+#define DTV_FE_UNC_UNIT		52
+
+#define DTV_MAX_COMMAND				DTV_FE_UNC_UNIT
 
 typedef enum fe_pilot {
 	PILOT_ON,
@@ -338,6 +350,46 @@  typedef enum fe_delivery_system {
 	SYS_DAB,
 } fe_delivery_system_t;
 
+/* Frontend General Statistics
+ * General parameters
+ * FE_*_UNKNOWN:
+ *	Parameter is unknown to the frontend and doesn't really
+ *	make any sense for an application.
+ *
+ * FE_*_RELATIVE:
+ *	Parameter is relative on the basis of a ceil - floor basis
+ *	Format is based on empirical test to determine
+ *	the floor and ceiling values. This format is exactly the
+ *	same format as the existing statistics implementation.
+ */
+
+enum fecap_quality_params {
+	FE_QUALITY_UNKNOWN		= 0,
+	FE_QUALITY_SNR			= (1 <<  0),
+	FE_QUALITY_CNR			= (1 <<  1),
+	FE_QUALITY_EsNo			= (1 <<  2),
+	FE_QUALITY_EbNo			= (1 <<  3),
+	FE_QUALITY_RELATIVE		= (1 << 31),
+};
+
+enum fecap_scale_params {
+	FE_SCALE_UNKNOWN		= 0,
+	FE_SCALE_dB			= (1 <<  0),
+	FE_SCALE_RELATIVE		= (1 << 31),
+};
+
+enum fecap_error_params {
+	FE_ERROR_UNKNOWN		= 0,
+	FE_ERROR_BER			= (1 <<  0),
+	FE_ERROR_PER			= (1 <<  1),
+	FE_ERROR_RELATIVE		= (1 << 31),
+};
+
+enum fecap_unc_params {
+	FE_UNC_UNKNOWN			= 0,
+	FE_UNC_RELATIVE			= (1 << 31),
+};
+
 struct dtv_cmds_h {
 	char	*name;		/* A display name for debugging purposes */