V4L/DVB (8985): S2API: Added dvb frontend changes to support a newer tuning API
authorSteven Toth <stoth@linuxtv.org>
Thu, 4 Sep 2008 04:12:25 +0000 (01:12 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sun, 12 Oct 2008 11:37:03 +0000 (09:37 -0200)
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>
drivers/media/dvb/dvb-core/dvb_frontend.c
drivers/media/dvb/dvb-core/dvb_frontend.h
include/linux/dvb/frontend.h

index 3526e3e..e68974b 100644 (file)
@@ -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;
index aa4133f..61d53ee 100644 (file)
@@ -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,
index c8cbd90..4d37700 100644 (file)
@@ -62,6 +62,7 @@ typedef enum fe_caps {
        FE_CAN_HIERARCHY_AUTO           = 0x100000,
        FE_CAN_8VSB                     = 0x200000,
        FE_CAN_16VSB                    = 0x400000,
+       FE_HAS_EXTENDED_CAPS            = 0x800000,   // We need more bitspace for newer APIs, indicate this.
        FE_NEEDS_BENDING                = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending)
        FE_CAN_RECOVER                  = 0x40000000, // frontend can recover from a cable unplug automatically
        FE_CAN_MUTE_TS                  = 0x80000000  // frontend can stop spurious TS data output
@@ -147,7 +148,9 @@ typedef enum fe_code_rate {
        FEC_6_7,
        FEC_7_8,
        FEC_8_9,
-       FEC_AUTO
+       FEC_AUTO,
+       FEC_3_5,
+       FEC_9_10,
 } fe_code_rate_t;
 
 
@@ -160,7 +163,11 @@ typedef enum fe_modulation {
        QAM_256,
        QAM_AUTO,
        VSB_8,
-       VSB_16
+       VSB_16,
+       _8PSK,
+       _16APSK,
+       NBC_QPSK,
+       DQPSK,
 } fe_modulation_t;
 
 typedef enum fe_transmit_mode {
@@ -239,6 +246,125 @@ struct dvb_frontend_event {
        struct dvb_frontend_parameters parameters;
 };
 
+/* TODO: Turn this into a series of defines, so future maintainers
+ * don't insert random new commands and break backwards
+ * binary compatability.
+ */
+typedef enum tv_cmd_types {
+       TV_SEQ_UNDEFINED,
+       TV_SEQ_START,
+       TV_SEQ_CONTINUE,
+       TV_SEQ_COMPLETE,
+       TV_SEQ_TERMINATE,
+
+       TV_SET_FREQUENCY,
+       TV_SET_MODULATION,
+       TV_SET_BANDWIDTH,
+       TV_SET_INVERSION,
+       TV_SET_DISEQC_MASTER,
+       TV_SET_SYMBOLRATE,
+       TV_SET_INNERFEC,
+       TV_SET_VOLTAGE,
+       TV_SET_TONE,
+       TV_SET_PILOT,
+       TV_SET_ROLLOFF,
+
+       TV_GET_FREQUENCY,
+       TV_GET_MODULATION,
+       TV_GET_BANDWIDTH,
+       TV_GET_INVERSION,
+       TV_GET_DISEQC_SLAVE_REPLY,
+       TV_GET_SYMBOLRATE,
+       TV_GET_INNERFEC,
+       TV_GET_VOLTAGE,
+       TV_GET_TONE,
+       TV_GET_PILOT,
+       TV_GET_ROLLOFF,
+
+       /* Basic enumeration set for querying unlimited capabilities */
+       TV_GET_FE_CAPABILITY_COUNT,
+       TV_GET_FE_CAPABILITY,
+
+       /* New commands are always appended */
+       TV_SET_DELIVERY_SYSTEM,
+       TV_GET_DELIVERY_SYSTEM,
+
+       /* ISDB-T */
+       TV_SET_ISDB_SEGMENT_NUM,
+       TV_GET_ISDB_SEGMENT_NUM,
+       TV_SET_ISDB_SEGMENT_WIDTH,
+       TV_GET_ISDB_SEGMENT_WIDTH,
+       TV_GET_ISDB_LAYERA_FEC,
+       TV_GET_ISDB_LAYERA_MODULATION,
+       TV_GET_ISDB_LAYERA_SEGMENT_WIDTH,
+       TV_GET_ISDB_LAYERB_FEC,
+       TV_GET_ISDB_LAYERB_MODULATION,
+       TV_GET_ISDB_LAYERB_SEGMENT_WIDTH,
+       TV_GET_ISDB_LAYERC_FEC,
+       TV_GET_ISDB_LAYERC_MODULATION,
+       TV_GET_ISDB_LAYERC_SEGMENT_WIDTH,
+
+} tv_cmd_types_t;
+
+typedef enum fe_pilot {
+       PILOT_ON,
+       PILOT_OFF,
+       PILOT_AUTO,
+} fe_pilot_t;
+
+typedef enum fe_rolloff {
+       ROLLOFF_20,
+       ROLLOFF_25,
+       ROLLOFF_35,
+       ROLLOFF_AUTO,
+} fe_rolloff_t;
+
+typedef enum fe_delivery_system {
+       SYS_UNDEFINED,
+       SYS_DVBC_ANNEX_AC,
+       SYS_DVBC_ANNEX_B,
+       SYS_DVBT,
+       SYS_DVBS,
+       SYS_DVBS2,
+       SYS_DVBH,
+       SYS_ISDBT,
+       SYS_ISDBS,
+       SYS_ISDBC,
+       SYS_ATSC,
+       SYS_ATSCMH,
+       SYS_DMBTH,
+       SYS_CMMB,
+       SYS_DAB,
+} fe_delivery_system_t;
+
+struct tv_cmds_h {
+       char    *name;          /* A display name for debugging purposes */
+
+       __u32   cmd;            /* A unique ID */
+
+       /* Flags */
+       __u32   set:1;          /* Either a set or get property */
+       __u32   buffer:1;       /* Does this property use the buffer? */
+       __u32   reserved:30;    /* Align */
+};
+
+typedef struct {
+       __u32 cmd;
+       union {
+               __u32 data;
+               struct {
+                       __u8 data[32];
+                       __u32 len;
+               } buffer;
+       } u;
+} tv_property_t;
+
+/* No more than 16 properties during any given ioctl */
+typedef tv_property_t tv_properties_t[16];
+
+#define FE_SET_PROPERTY                   _IOW('o', 82, tv_properties_t)
+#define FE_GET_PROPERTY                   _IOR('o', 83, tv_properties_t)
+
 
 /**
  * When set, this flag will disable any zigzagging or other "normal" tuning