Michael Krufky wrote: > On Fri, Dec 4, 2009 at 3:02 PM, VDR User <user.vdr@xxxxxxxxx> wrote: >> No activity in this thread for 2 weeks now. Has there been any progress? > I have stated that I like Manu's proposal, but I would prefer that the > get_property (s2api) interface were used, because it totally provides > an interface that is sufficient for this feature. I've ported Manu's proposal to S2API way of handling it. It is just compiled only. I haven't test it yet on a real driver. Comments? --- Add support for frontend statistics via S2API The current DVB V3 API to handle statistics has two issues: - Retrieving several values can't be done atomically; - There's no indication about scale information. This patch solves those two issues by adding a group of S2API that handles the needed statistics operations. It basically ports the proposal of Manu Abraham <abraham.manu@xxxxxxxxx> To S2API. As the original patch, both of the above issues were addressed. In order to demonstrate the changes on an existing driver for the new API, I've implemented it at the cx24123 driver. There are some advantages of using this approach over using the static structs of the original proposal: - userspace can select an arbitrary number of parameters on his get request; - the latency to retrieve just one parameter is lower than retrieving several parameters. On the cx24123 example, if user wants just signal strength, the latency is the same as reading one register via i2c bus. If using the original proposal, the latency would be 6 times worse, since you would need to get 3 properties at the same time; - the latency for reading all 3 parameters at the same time is equal to the latency of the original proposal; - if newer statistics parameters will be needed in the future, it is just a matter of adding additional S2API command/value pairs; - the DVB V3 calls can be easily implemented as a call to the new get_stats ops, without adding extra latency time. Thanks to Manu Abraham <abraham.manu@xxxxxxxxx> for his initial proposal. Signed-off-by: Mauro Carvalho Chehab <mchehab@xxxxxxxxxx> 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 */ -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html