summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSteven Toth <stoth@linuxtv.org>2008-09-04 01:12:25 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-10-12 09:37:03 -0200
commit6b73eeafbc856c0cef7166242f0e55403407f355 (patch)
treed385a9c8c8eb0682c2690ca0b2352bea96349f52 /drivers
parent05c1cab55c282199b85138dc1900b5d9bd6fb32a (diff)
V4L/DVB (8985): S2API: Added dvb frontend changes to support a newer tuning API
This is an experimental patch to add a new tuning mechanism for dvb frontends. Rather than passing fixed structures across the user/kernel boundary, which need to be revised for each new modulation type (or feature the kernel developers want to add), this implements a simpler message based approach, allowing fe commands to be broken down into a series of small fixed size transactions, presented in an array. The goal is to avoid changing the user/kernel ABI in the future, by simply creating new frontend commands (and sequencies of commands) that help us add support for brand new demodulator, delivery system or statistics related commmands. known issues: checkpatch voilations feedback from various developers yet to be implemented, relating to namespace conventions, variable length array passing conventions, and generally some optimization. This patch should support all existing tuning mechanisms through the new API, as well as adding 8PSK, DVB-S2 NBC-QPSK and ISDB-T API support. For testing and exercise purposes, see the latest tune.c tool available from http://www.steventoth.net/linux/s2 Signed-off-by: Steven Toth <stoth@linuxtv.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c571
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h44
2 files changed, 609 insertions, 6 deletions
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 3526e3ee9487..e68974b2fee9 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -755,6 +755,535 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
return 0;
}
+struct tv_cmds_h tv_cmds[] = {
+ [TV_SEQ_UNDEFINED] = {
+ .name = "TV_SEQ_UNDEFINED",
+ .cmd = TV_SEQ_UNDEFINED,
+ .set = 1,
+ },
+ [TV_SEQ_START] = {
+ .name = "TV_SEQ_START",
+ .cmd = TV_SEQ_START,
+ .set = 1,
+ },
+ [TV_SEQ_CONTINUE] = {
+ .name = "TV_SEQ_CONTINUE",
+ .cmd = TV_SEQ_CONTINUE,
+ .set = 1,
+ },
+ [TV_SEQ_COMPLETE] = {
+ .name = "TV_SEQ_COMPLETE",
+ .cmd = TV_SEQ_COMPLETE,
+ .set = 1,
+ },
+ [TV_SEQ_TERMINATE] = {
+ .name = "TV_SEQ_TERMINATE",
+ .cmd = TV_SEQ_TERMINATE,
+ .set = 1,
+ },
+
+ /* Set */
+ [TV_SET_FREQUENCY] = {
+ .name = "TV_SET_FREQUENCY",
+ .cmd = TV_SET_FREQUENCY,
+ .set = 1,
+ },
+ [TV_SET_BANDWIDTH] = {
+ .name = "TV_SET_BANDWIDTH",
+ .cmd = TV_SET_BANDWIDTH,
+ .set = 1,
+ },
+ [TV_SET_MODULATION] = {
+ .name = "TV_SET_MODULATION",
+ .cmd = TV_SET_MODULATION,
+ .set = 1,
+ },
+ [TV_SET_INVERSION] = {
+ .name = "TV_SET_INVERSION",
+ .cmd = TV_SET_INVERSION,
+ .set = 1,
+ },
+ [TV_SET_DISEQC_MASTER] = {
+ .name = "TV_SET_DISEQC_MASTER",
+ .cmd = TV_SET_DISEQC_MASTER,
+ .set = 1,
+ .buffer = 1,
+ },
+ [TV_SET_SYMBOLRATE] = {
+ .name = "TV_SET_SYMBOLRATE",
+ .cmd = TV_SET_SYMBOLRATE,
+ .set = 1,
+ },
+ [TV_SET_INNERFEC] = {
+ .name = "TV_SET_INNERFEC",
+ .cmd = TV_SET_INNERFEC,
+ .set = 1,
+ },
+ [TV_SET_VOLTAGE] = {
+ .name = "TV_SET_VOLTAGE",
+ .cmd = TV_SET_VOLTAGE,
+ .set = 1,
+ },
+ [TV_SET_TONE] = {
+ .name = "TV_SET_TONE",
+ .cmd = TV_SET_TONE,
+ .set = 1,
+ },
+ [TV_SET_PILOT] = {
+ .name = "TV_SET_PILOT",
+ .cmd = TV_SET_PILOT,
+ .set = 1,
+ },
+ [TV_SET_ROLLOFF] = {
+ .name = "TV_SET_ROLLOFF",
+ .cmd = TV_SET_ROLLOFF,
+ .set = 1,
+ },
+ [TV_SET_DELIVERY_SYSTEM] = {
+ .name = "TV_SET_DELIVERY_SYSTEM",
+ .cmd = TV_SET_DELIVERY_SYSTEM,
+ .set = 1,
+ },
+ [TV_SET_ISDB_SEGMENT_NUM] = {
+ .name = "TV_SET_ISDB_SEGMENT_NUM",
+ .cmd = TV_SET_ISDB_SEGMENT_NUM,
+ .set = 1,
+ },
+ [TV_SET_ISDB_SEGMENT_WIDTH] = {
+ .name = "TV_SET_ISDB_SEGMENT_WIDTH",
+ .cmd = TV_SET_ISDB_SEGMENT_WIDTH,
+ .set = 1,
+ },
+
+ /* Get */
+ [TV_GET_FREQUENCY] = {
+ .name = "TV_GET_FREQUENCY",
+ .cmd = TV_GET_FREQUENCY,
+ .set = 0,
+ },
+ [TV_GET_BANDWIDTH] = {
+ .name = "TV_GET_BANDWIDTH",
+ .cmd = TV_GET_BANDWIDTH,
+ .set = 0,
+ },
+ [TV_GET_MODULATION] = {
+ .name = "TV_GET_MODULATION",
+ .cmd = TV_GET_MODULATION,
+ .set = 0,
+ },
+ [TV_GET_INVERSION] = {
+ .name = "TV_GET_INVERSION",
+ .cmd = TV_GET_INVERSION,
+ .set = 0,
+ },
+ [TV_GET_DISEQC_SLAVE_REPLY] = {
+ .name = "TV_GET_DISEQC_SLAVE_REPLY",
+ .cmd = TV_GET_DISEQC_SLAVE_REPLY,
+ .set = 0,
+ .buffer = 1,
+ },
+ [TV_GET_SYMBOLRATE] = {
+ .name = "TV_GET_SYMBOLRATE",
+ .cmd = TV_GET_SYMBOLRATE,
+ .set = 0,
+ },
+ [TV_GET_INNERFEC] = {
+ .name = "TV_GET_INNERFEC",
+ .cmd = TV_GET_INNERFEC,
+ .set = 0,
+ },
+ [TV_GET_VOLTAGE] = {
+ .name = "TV_GET_VOLTAGE",
+ .cmd = TV_GET_VOLTAGE,
+ .set = 0,
+ },
+ [TV_GET_TONE] = {
+ .name = "TV_GET_TONE",
+ .cmd = TV_GET_TONE,
+ .set = 0,
+ },
+ [TV_GET_PILOT] = {
+ .name = "TV_GET_PILOT",
+ .cmd = TV_GET_PILOT,
+ .set = 0,
+ },
+ [TV_GET_ROLLOFF] = {
+ .name = "TV_GET_ROLLOFF",
+ .cmd = TV_GET_ROLLOFF,
+ .set = 0,
+ },
+ [TV_GET_DELIVERY_SYSTEM] = {
+ .name = "TV_GET_DELIVERY_SYSTEM",
+ .cmd = TV_GET_DELIVERY_SYSTEM,
+ .set = 0,
+ },
+ [TV_GET_ISDB_SEGMENT_NUM] = {
+ .name = "TV_GET_ISDB_SEGMENT_NUM",
+ .cmd = TV_GET_ISDB_SEGMENT_NUM,
+ .set = 0,
+ },
+ [TV_GET_ISDB_SEGMENT_WIDTH] = {
+ .name = "TV_GET_ISDB_SEGMENT_WIDTH",
+ .cmd = TV_GET_ISDB_SEGMENT_WIDTH,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERA_FEC] = {
+ .name = "TV_GET_ISDB_LAYERA_FEC",
+ .cmd = TV_GET_ISDB_LAYERA_FEC,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERA_MODULATION] = {
+ .name = "TV_GET_ISDB_LAYERA_MODULATION",
+ .cmd = TV_GET_ISDB_LAYERA_MODULATION,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = {
+ .name = "TV_GET_ISDB_LAYERA_SEGMENT_WIDTH",
+ .cmd = TV_GET_ISDB_LAYERA_SEGMENT_WIDTH,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERB_FEC] = {
+ .name = "TV_GET_ISDB_LAYERB_FEC",
+ .cmd = TV_GET_ISDB_LAYERB_FEC,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERB_MODULATION] = {
+ .name = "TV_GET_ISDB_LAYERB_MODULATION",
+ .cmd = TV_GET_ISDB_LAYERB_MODULATION,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = {
+ .name = "TV_GET_ISDB_LAYERB_SEGMENT_WIDTH",
+ .cmd = TV_GET_ISDB_LAYERB_SEGMENT_WIDTH,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERC_FEC] = {
+ .name = "TV_GET_ISDB_LAYERC_FEC",
+ .cmd = TV_GET_ISDB_LAYERC_FEC,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERC_MODULATION] = {
+ .name = "TV_GET_ISDB_LAYERC_MODULATION",
+ .cmd = TV_GET_ISDB_LAYERC_MODULATION,
+ .set = 0,
+ },
+ [TV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = {
+ .name = "TV_GET_ISDB_LAYERC_SEGMENT_WIDTH",
+ .cmd = TV_GET_ISDB_LAYERC_SEGMENT_WIDTH,
+ .set = 0,
+ },
+};
+
+void tv_property_dump(tv_property_t *tvp)
+{
+ int i;
+
+ printk("%s() tvp.cmd = 0x%08x (%s)\n"
+ ,__FUNCTION__
+ ,tvp->cmd
+ ,tv_cmds[ tvp->cmd ].name);
+
+ if(tv_cmds[ tvp->cmd ].buffer) {
+
+ printk("%s() tvp.u.buffer.len = 0x%02x\n"
+ ,__FUNCTION__
+ ,tvp->u.buffer.len);
+
+ for(i = 0; i < tvp->u.buffer.len; i++)
+ printk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n"
+ ,__FUNCTION__
+ ,i
+ ,tvp->u.buffer.data[i]);
+
+ } else
+ printk("%s() tvp.u.data = 0x%08x\n", __FUNCTION__, tvp->u.data);
+}
+
+int is_legacy_delivery_system(fe_delivery_system_t s)
+{
+ if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) ||
+ (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS))
+ return 1;
+
+ return 0;
+}
+
+int tv_property_cache_submit(struct dvb_frontend *fe)
+{
+
+ /* We have to do one of two things:
+ * To support legacy devices using the new API we take values from
+ * the tv_cache and generate a legacy truning structure.
+ *
+ * Or,
+ *
+ * To support advanced tuning devices with the new API we
+ * notify the new advance driver type that a tuning operation is required
+ * and let it pull values from the cache as is, we don't need to
+ * pass structures.
+ *
+ * We'll use the modulation type to assess how this is handled. as the API
+ * progresses we'll probably want to have a flag in dvb_frontend_ops
+ * to allow the frontend driver to dictate how it likes to be tuned.
+ *
+ * Because of how this is attached to the ioctl handler for legacy support,
+ * it's important to return an appropriate result code with atleast the following
+ * three meanings:
+ * < 0 = processing error
+ * 0 = lecagy ioctl handler to submit a traditional set_frontend() call.
+ * 1 = lecagy ioctl handler should NOT submit a traditional set_frontend() call.
+ */
+
+ int r;
+
+ struct tv_frontend_properties *c = &fe->tv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_frontend_parameters p;
+
+ printk("%s()\n", __FUNCTION__);
+
+ /* For legacy delivery systems we don't need the delivery_system to be specified */
+ if(is_legacy_delivery_system(c->delivery_system)) {
+ switch(c->modulation) {
+ case QPSK:
+ printk("%s() Preparing QPSK req\n", __FUNCTION__);
+ p.frequency = c->frequency;
+ p.inversion = c->inversion;
+ p.u.qpsk.symbol_rate = c->symbol_rate;
+ p.u.qpsk.fec_inner = c->fec_inner;
+ memcpy(&fepriv->parameters, &p,
+ sizeof (struct dvb_frontend_parameters));
+
+ /* Call the traditional tuning mechanisms. */
+
+ r = 0;
+ break;
+ case QAM_16:
+ case QAM_32:
+ case QAM_64:
+ case QAM_128:
+ case QAM_256:
+ case QAM_AUTO:
+ printk("%s() Preparing QAM req\n", __FUNCTION__);
+ p.frequency = c->frequency;
+ p.inversion = c->inversion;
+ p.u.qam.symbol_rate = c->symbol_rate;
+ p.u.vsb.modulation = c->modulation;
+ printk("%s() frequency = %d\n", __FUNCTION__, p.frequency);
+ printk("%s() QAM = %d\n", __FUNCTION__, p.u.vsb.modulation);
+ memcpy(&fepriv->parameters, &p,
+ sizeof (struct dvb_frontend_parameters));
+
+ /* At this point we're fully formed for backwards
+ * compatability and we need to return this
+ * via the ioctl handler as SET_FRONTEND (arg).
+ * We've already patched the new values into the
+ * frontends tuning structures so the ioctl code just
+ * continues as if a legacy tune structure was passed
+ * from userspace.
+ */
+
+ r = 0;
+ break;
+ case VSB_8:
+ case VSB_16:
+ printk("%s() Preparing VSB req\n", __FUNCTION__);
+ p.frequency = c->frequency;
+ p.u.vsb.modulation = c->modulation;
+ memcpy(&fepriv->parameters, &p,
+ sizeof (struct dvb_frontend_parameters));
+
+ /* Call the traditional tuning mechanisms. */
+
+ r = 0;
+ break;
+ /* TODO: Add any missing modulation types */
+ default:
+ r = -1;
+ }
+ } else {
+ /* For advanced delivery systems / modulation types ...
+ * we seed the lecacy dvb_frontend_parameters structure
+ * so that the sanity checking code later in the IOCTL processing
+ * can validate our basic frequency ranges, symbolrates, modulation
+ * etc.
+ */
+ r = -1;
+
+ switch(c->modulation) {
+ case _8PSK:
+ case _16APSK:
+ case NBC_QPSK:
+ /* Just post a notification to the demod driver and let it pull
+ * the specific values it wants from its tv_property_cache.
+ * It can decide how best to use those parameters.
+ * IOCTL will call set_frontend (by default) due to zigzag
+ * support etc.
+ */
+ if (fe->ops.set_params)
+ r = fe->ops.set_params(fe);
+
+ p.frequency = c->frequency;
+ p.inversion = c->inversion;
+ p.u.qpsk.symbol_rate = c->symbol_rate;
+ p.u.qpsk.fec_inner = c->fec_inner;
+ memcpy(&fepriv->parameters, &p,
+ sizeof (struct dvb_frontend_parameters));
+
+ r = 0;
+ break;
+ default:
+ r = -1;
+ }
+
+ if(c->delivery_system == SYS_ISDBT) {
+ /* Fake out a generic DVB-T request so we pass validation in the ioctl */
+ p.frequency = c->frequency;
+ p.inversion = INVERSION_AUTO;
+ p.u.ofdm.constellation = QAM_AUTO;
+ p.u.ofdm.code_rate_HP = FEC_AUTO;
+ p.u.ofdm.code_rate_LP = FEC_AUTO;
+ p.u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ p.u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+ p.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+ p.u.ofdm.hierarchy_information = HIERARCHY_AUTO;
+ memcpy(&fepriv->parameters, &p,
+ sizeof (struct dvb_frontend_parameters));
+
+ r = 0;
+ }
+ }
+ return r;
+}
+
+int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp)
+{
+ int r = 0;
+ printk("%s()\n", __FUNCTION__);
+ tv_property_dump(tvp);
+
+ switch(tvp->cmd) {
+ case TV_SEQ_START:
+ case TV_SEQ_TERMINATE:
+ /* Reset a cache of data specific to the frontend here. This does
+ * not effect hardware.
+ */
+ printk("%s() Flushing property cache\n", __FUNCTION__);
+ memset(&fe->tv_property_cache, 0, sizeof(struct tv_frontend_properties));
+ fe->tv_property_cache.state = TV_SEQ_START;
+ fe->tv_property_cache.delivery_system = SYS_UNDEFINED;
+ break;
+ case TV_SEQ_COMPLETE:
+ /* interpret the cache of data, build either a traditional frontend
+ * tunerequest and submit it to a subset of the ioctl handler,
+ * or, call a new undefined method on the frontend to deal with
+ * all new tune requests.
+ */
+ fe->tv_property_cache.state = TV_SEQ_COMPLETE;
+ printk("%s() Finalised property cache\n", __FUNCTION__);
+ r = tv_property_cache_submit(fe);
+ break;
+ case TV_SET_FREQUENCY:
+ fe->tv_property_cache.frequency = tvp->u.data;
+ break;
+ case TV_GET_FREQUENCY:
+ tvp->u.data = fe->tv_property_cache.frequency;
+ break;
+ case TV_SET_MODULATION:
+ fe->tv_property_cache.modulation = tvp->u.data;
+ break;
+ case TV_GET_MODULATION:
+ tvp->u.data = fe->tv_property_cache.modulation;
+ break;
+ case TV_SET_BANDWIDTH:
+ fe->tv_property_cache.bandwidth = tvp->u.data;
+ break;
+ case TV_GET_BANDWIDTH:
+ tvp->u.data = fe->tv_property_cache.bandwidth;
+ break;
+ case TV_SET_INVERSION:
+ fe->tv_property_cache.inversion = tvp->u.data;
+ break;
+ case TV_GET_INVERSION:
+ tvp->u.data = fe->tv_property_cache.inversion;
+ break;
+ case TV_SET_SYMBOLRATE:
+ fe->tv_property_cache.symbol_rate = tvp->u.data;
+ break;
+ case TV_GET_SYMBOLRATE:
+ tvp->u.data = fe->tv_property_cache.symbol_rate;
+ break;
+ case TV_SET_INNERFEC:
+ fe->tv_property_cache.fec_inner = tvp->u.data;
+ break;
+ case TV_GET_INNERFEC:
+ tvp->u.data = fe->tv_property_cache.fec_inner;
+ break;
+ case TV_SET_PILOT:
+ fe->tv_property_cache.pilot = tvp->u.data;
+ break;
+ case TV_GET_PILOT:
+ tvp->u.data = fe->tv_property_cache.pilot;
+ break;
+ case TV_SET_ROLLOFF:
+ fe->tv_property_cache.rolloff = tvp->u.data;
+ break;
+ case TV_GET_ROLLOFF:
+ tvp->u.data = fe->tv_property_cache.rolloff;
+ break;
+ case TV_SET_DELIVERY_SYSTEM:
+ fe->tv_property_cache.delivery_system = tvp->u.data;
+ break;
+ case TV_GET_DELIVERY_SYSTEM:
+ tvp->u.data = fe->tv_property_cache.delivery_system;
+ break;
+
+ /* ISDB-T Support here */
+ case TV_SET_ISDB_SEGMENT_NUM:
+ fe->tv_property_cache.isdb_segment_num = tvp->u.data;
+ break;
+ case TV_GET_ISDB_SEGMENT_NUM:
+ tvp->u.data = fe->tv_property_cache.isdb_segment_num;
+ break;
+ case TV_SET_ISDB_SEGMENT_WIDTH:
+ fe->tv_property_cache.isdb_segment_width = tvp->u.data;
+ break;
+ case TV_GET_ISDB_SEGMENT_WIDTH:
+ tvp->u.data = fe->tv_property_cache.isdb_segment_width;
+ break;
+ case TV_GET_ISDB_LAYERA_FEC:
+ tvp->u.data = fe->tv_property_cache.isdb_layera_fec;
+ break;
+ case TV_GET_ISDB_LAYERA_MODULATION:
+ tvp->u.data = fe->tv_property_cache.isdb_layera_modulation;
+ break;
+ case TV_GET_ISDB_LAYERA_SEGMENT_WIDTH:
+ tvp->u.data = fe->tv_property_cache.isdb_layera_segment_width;
+ break;
+ case TV_GET_ISDB_LAYERB_FEC:
+ tvp->u.data = fe->tv_property_cache.isdb_layerb_fec;
+ break;
+ case TV_GET_ISDB_LAYERB_MODULATION:
+ tvp->u.data = fe->tv_property_cache.isdb_layerb_modulation;
+ break;
+ case TV_GET_ISDB_LAYERB_SEGMENT_WIDTH:
+ tvp->u.data = fe->tv_property_cache.isdb_layerb_segment_width;
+ break;
+ case TV_GET_ISDB_LAYERC_FEC:
+ tvp->u.data = fe->tv_property_cache.isdb_layerc_fec;
+ break;
+ case TV_GET_ISDB_LAYERC_MODULATION:
+ tvp->u.data = fe->tv_property_cache.isdb_layerc_modulation;
+ break;
+ case TV_GET_ISDB_LAYERC_SEGMENT_WIDTH:
+ tvp->u.data = fe->tv_property_cache.isdb_layerc_segment_width;
+ break;
+
+ }
+
+ return 0;
+}
+
static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *parg)
{
@@ -762,6 +1291,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
struct dvb_frontend *fe = dvbdev->priv;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
int err = -EOPNOTSUPP;
+ tv_property_t* tvp;
dprintk ("%s\n", __func__);
@@ -776,6 +1306,27 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
if (down_interruptible (&fepriv->sem))
return -ERESTARTSYS;
+ if(cmd == FE_SET_PROPERTY) {
+ printk("%s() FE_SET_PROPERTY\n", __FUNCTION__);
+
+ /* TODO: basic property validation here */
+
+ /* TODO: ioctl userdata out of range check here */
+ tvp = parg;
+ while(tvp->cmd != TV_SEQ_UNDEFINED) {
+ tv_property_process(fe, tvp);
+ if( (tvp->cmd == TV_SEQ_TERMINATE) || (tvp->cmd == TV_SEQ_COMPLETE) )
+ break;
+ tvp++;
+ }
+
+ if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) {
+ printk("%s() Property cache is full, tuning\n", __FUNCTION__);
+ cmd = FE_SET_FRONTEND;
+ }
+ err = 0;
+ }
+
switch (cmd) {
case FE_GET_INFO: {
struct dvb_frontend_info* info = parg;
@@ -942,13 +1493,20 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
case FE_SET_FRONTEND: {
struct dvb_frontend_tune_settings fetunesettings;
- if (dvb_frontend_check_parameters(fe, parg) < 0) {
- err = -EINVAL;
- break;
- }
+ if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) {
+ if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) {
+ err = -EINVAL;
+ break;
+ }
+ } else {
+ if (dvb_frontend_check_parameters(fe, parg) < 0) {
+ err = -EINVAL;
+ break;
+ }
- memcpy (&fepriv->parameters, parg,
- sizeof (struct dvb_frontend_parameters));
+ memcpy (&fepriv->parameters, parg,
+ sizeof (struct dvb_frontend_parameters));
+ }
memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
memcpy(&fetunesettings.parameters, parg,
@@ -1031,6 +1589,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
return err;
}
+
static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
{
struct dvb_device *dvbdev = file->private_data;
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index aa4133f0bd19..61d53ee70f2a 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -169,6 +169,10 @@ struct dvb_frontend_ops {
struct dvb_tuner_ops tuner_ops;
struct analog_demod_ops analog_ops;
+
+ int (*set_property)(struct dvb_frontend* fe, tv_property_t* tvp);
+ int (*get_property)(struct dvb_frontend* fe, tv_property_t* tvp);
+ int (*set_params)(struct dvb_frontend* fe);
};
#define MAX_EVENT 8
@@ -182,6 +186,45 @@ struct dvb_fe_events {
struct mutex mtx;
};
+struct tv_frontend_properties {
+
+ /* Cache State */
+ u32 state;
+
+ u32 frequency;
+ fe_modulation_t modulation;
+
+ fe_sec_voltage_t voltage;
+ fe_sec_tone_mode_t sectone;
+ fe_spectral_inversion_t inversion;
+ fe_code_rate_t fec_inner;
+ fe_transmit_mode_t transmission_mode;
+ fe_bandwidth_t bandwidth;
+ fe_guard_interval_t guard_interval;
+ fe_hierarchy_t hierarchy;
+ u32 symbol_rate;
+ fe_code_rate_t code_rate_HP;
+ fe_code_rate_t code_rate_LP;
+
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
+
+ fe_delivery_system_t delivery_system;
+
+ /* ISDB-T specifics */
+ u32 isdb_segment_num;
+ u32 isdb_segment_width;
+ fe_code_rate_t isdb_layera_fec;
+ fe_modulation_t isdb_layera_modulation;
+ u32 isdb_layera_segment_width;
+ fe_code_rate_t isdb_layerb_fec;
+ fe_modulation_t isdb_layerb_modulation;
+ u32 isdb_layerb_segment_width;
+ fe_code_rate_t isdb_layerc_fec;
+ fe_modulation_t isdb_layerc_modulation;
+ u32 isdb_layerc_segment_width;
+};
+
struct dvb_frontend {
struct dvb_frontend_ops ops;
struct dvb_adapter *dvb;
@@ -190,6 +233,7 @@ struct dvb_frontend {
void *frontend_priv;
void *sec_priv;
void *analog_demod_priv;
+ struct tv_frontend_properties tv_property_cache;
};
extern int dvb_register_frontend(struct dvb_adapter *dvb,