V4L/DVB: fix dvb frontend lockup
[safe/jmp/linux-2.6] / drivers / media / dvb / dvb-core / dvb_frontend.c
index 925cfa6..6932def 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/wait.h>
 #include <linux/slab.h>
 #include <linux/poll.h>
+#include <linux/semaphore.h>
 #include <linux/module.h>
 #include <linux/list.h>
 #include <linux/freezer.h>
 
 #include "dvb_frontend.h"
 #include "dvbdev.h"
+#include <linux/dvb/version.h>
 
 static int dvb_frontend_debug;
 static int dvb_shutdown_timeout;
 static int dvb_force_auto_inversion;
 static int dvb_override_tune_delay;
 static int dvb_powerdown_on_sleep = 1;
+static int dvb_mfe_wait_time = 5;
 
 module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
 MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off).");
@@ -57,6 +60,8 @@ module_param(dvb_override_tune_delay, int, 0644);
 MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
 module_param(dvb_powerdown_on_sleep, int, 0644);
 MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)");
+module_param(dvb_mfe_wait_time, int, 0644);
+MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open() for multi-frontend to become available (default:5 seconds)");
 
 #define dprintk if (dvb_frontend_debug) printk
 
@@ -68,6 +73,7 @@ MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB volt
 #define FESTATE_ZIGZAG_FAST 32
 #define FESTATE_ZIGZAG_SLOW 64
 #define FESTATE_DISEQC 128
+#define FESTATE_ERROR 256
 #define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
 #define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
 #define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
@@ -89,6 +95,10 @@ MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB volt
  * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
  */
 
+#define DVB_FE_NO_EXIT 0
+#define DVB_FE_NORMAL_EXIT     1
+#define DVB_FE_DEVICE_REMOVED  2
+
 static DEFINE_MUTEX(frontend_mutex);
 
 struct dvb_frontend_private {
@@ -124,6 +134,7 @@ struct dvb_frontend_private {
        unsigned int step_size;
        int quality;
        unsigned int check_wrapped;
+       enum dvbfe_search algo_status;
 };
 
 static void dvb_frontend_wakeup(struct dvb_frontend *fe);
@@ -135,7 +146,7 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
        struct dvb_frontend_event *e;
        int wp;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        if (mutex_lock_interruptible (&events->mtx))
                return;
@@ -171,7 +182,7 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        struct dvb_fe_events *events = &fepriv->events;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        if (events->overflow) {
                events->overflow = 0;
@@ -211,13 +222,16 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
 
 static void dvb_frontend_init(struct dvb_frontend *fe)
 {
-       dprintk ("DVB: initialising frontend %i (%s)...\n",
+       dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n",
                 fe->dvb->num,
+                fe->id,
                 fe->ops.info.name);
 
        if (fe->ops.init)
                fe->ops.init(fe);
        if (fe->ops.tuner_ops.init) {
+               if (fe->ops.i2c_gate_ctrl)
+                       fe->ops.i2c_gate_ctrl(fe, 1);
                fe->ops.tuner_ops.init(fe);
                if (fe->ops.i2c_gate_ctrl)
                        fe->ops.i2c_gate_ctrl(fe, 0);
@@ -237,7 +251,7 @@ static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepr
 {
        int q2;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        if (locked)
                (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256;
@@ -261,6 +275,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
 {
        int autoinversion;
        int ready = 0;
+       int fe_set_err = 0;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        int original_inversion = fepriv->parameters.inversion;
        u32 original_frequency = fepriv->parameters.frequency;
@@ -329,7 +344,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
 
        dprintk("%s: drift:%i inversion:%i auto_step:%i "
                "auto_sub_step:%i started_auto_step:%i\n",
-               __FUNCTION__, fepriv->lnb_drift, fepriv->inversion,
+               __func__, fepriv->lnb_drift, fepriv->inversion,
                fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step);
 
        /* set the frontend itself */
@@ -337,7 +352,11 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
        if (autoinversion)
                fepriv->parameters.inversion = fepriv->inversion;
        if (fe->ops.set_frontend)
-               fe->ops.set_frontend(fe, &fepriv->parameters);
+               fe_set_err = fe->ops.set_frontend(fe, &fepriv->parameters);
+       if (fe_set_err < 0) {
+               fepriv->state = FESTATE_ERROR;
+               return fe_set_err;
+       }
 
        fepriv->parameters.frequency = original_frequency;
        fepriv->parameters.inversion = original_inversion;
@@ -349,6 +368,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
 static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
 {
        fe_status_t s = 0;
+       int retval = 0;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
 
        /* if we've got no parameters, just keep idling */
@@ -362,8 +382,12 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
        if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) {
                if (fepriv->state & FESTATE_RETUNE) {
                        if (fe->ops.set_frontend)
-                               fe->ops.set_frontend(fe, &fepriv->parameters);
-                       fepriv->state = FESTATE_TUNED;
+                               retval = fe->ops.set_frontend(fe,
+                                                       &fepriv->parameters);
+                       if (retval < 0)
+                               fepriv->state = FESTATE_ERROR;
+                       else
+                               fepriv->state = FESTATE_TUNED;
                }
                fepriv->delay = 3*HZ;
                fepriv->quality = 0;
@@ -441,7 +465,11 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
                fepriv->delay = fepriv->min_delay;
 
                /* peform a tune */
-               if (dvb_frontend_swzigzag_autotune(fe, fepriv->check_wrapped)) {
+               retval = dvb_frontend_swzigzag_autotune(fe,
+                                                       fepriv->check_wrapped);
+               if (retval < 0) {
+                       return;
+               } else if (retval) {
                        /* OK, if we've run out of trials at the fast speed.
                         * Drop back to slow for the _next_ attempt */
                        fepriv->state = FESTATE_SEARCHING_SLOW;
@@ -473,7 +501,7 @@ static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
 {
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
 
-       if (fepriv->exit)
+       if (fepriv->exit != DVB_FE_NO_EXIT)
                return 1;
 
        if (fepriv->dvbdev->writers == 1)
@@ -509,9 +537,11 @@ static int dvb_frontend_thread(void *data)
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        unsigned long timeout;
        fe_status_t s;
+       enum dvbfe_algo algo;
+
        struct dvb_frontend_parameters *params;
 
-       dprintk("%s\n", __FUNCTION__);
+       dprintk("%s\n", __func__);
 
        fepriv->check_wrapped = 0;
        fepriv->quality = 0;
@@ -533,6 +563,7 @@ restart:
 
                if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
                        /* got signal or quitting */
+                       fepriv->exit = DVB_FE_NORMAL_EXIT;
                        break;
                }
 
@@ -555,29 +586,88 @@ restart:
 
                /* do an iteration of the tuning loop */
                if (fe->ops.get_frontend_algo) {
-                       if (fe->ops.get_frontend_algo(fe) == FE_ALGO_HW) {
-                               /* have we been asked to retune? */
-                               params = NULL;
+                       algo = fe->ops.get_frontend_algo(fe);
+                       switch (algo) {
+                       case DVBFE_ALGO_HW:
+                               dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
+                               params = NULL; /* have we been asked to RETUNE ? */
+
                                if (fepriv->state & FESTATE_RETUNE) {
+                                       dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
                                        params = &fepriv->parameters;
                                        fepriv->state = FESTATE_TUNED;
                                }
 
-                               fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
-                               if (s != fepriv->status) {
+                               if (fe->ops.tune)
+                                       fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
+
+                               if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
+                                       dprintk("%s: state changed, adding current state\n", __func__);
                                        dvb_frontend_add_event(fe, s);
                                        fepriv->status = s;
                                }
-                       } else
+                               break;
+                       case DVBFE_ALGO_SW:
+                               dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
                                dvb_frontend_swzigzag(fe);
-               } else
+                               break;
+                       case DVBFE_ALGO_CUSTOM:
+                               params = NULL; /* have we been asked to RETUNE ?        */
+                               dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
+                               if (fepriv->state & FESTATE_RETUNE) {
+                                       dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__);
+                                       params = &fepriv->parameters;
+                                       fepriv->state = FESTATE_TUNED;
+                               }
+                               /* Case where we are going to search for a carrier
+                                * User asked us to retune again for some reason, possibly
+                                * requesting a search with a new set of parameters
+                                */
+                               if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
+                                       if (fe->ops.search) {
+                                               fepriv->algo_status = fe->ops.search(fe, &fepriv->parameters);
+                                               /* We did do a search as was requested, the flags are
+                                                * now unset as well and has the flags wrt to search.
+                                                */
+                                       } else {
+                                               fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
+                                       }
+                               }
+                               /* Track the carrier if the search was successful */
+                               if (fepriv->algo_status == DVBFE_ALGO_SEARCH_SUCCESS) {
+                                       if (fe->ops.track)
+                                               fe->ops.track(fe, &fepriv->parameters);
+                               } else {
+                                       fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+                                       fepriv->delay = HZ / 2;
+                               }
+                               fe->ops.read_status(fe, &s);
+                               if (s != fepriv->status) {
+                                       dvb_frontend_add_event(fe, s); /* update event list */
+                                       fepriv->status = s;
+                                       if (!(s & FE_HAS_LOCK)) {
+                                               fepriv->delay = HZ / 10;
+                                               fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+                                       } else {
+                                               fepriv->delay = 60 * HZ;
+                                       }
+                               }
+                               break;
+                       default:
+                               dprintk("%s: UNDEFINED ALGO !\n", __func__);
+                               break;
+                       }
+               } else {
                        dvb_frontend_swzigzag(fe);
+               }
        }
 
        if (dvb_powerdown_on_sleep) {
                if (fe->ops.set_voltage)
                        fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
                if (fe->ops.tuner_ops.sleep) {
+                       if (fe->ops.i2c_gate_ctrl)
+                               fe->ops.i2c_gate_ctrl(fe, 1);
                        fe->ops.tuner_ops.sleep(fe);
                        if (fe->ops.i2c_gate_ctrl)
                                fe->ops.i2c_gate_ctrl(fe, 0);
@@ -587,6 +677,10 @@ restart:
        }
 
        fepriv->thread = NULL;
+       if (kthread_should_stop())
+               fepriv->exit = DVB_FE_DEVICE_REMOVED;
+       else
+               fepriv->exit = DVB_FE_NO_EXIT;
        mb();
 
        dvb_frontend_wakeup(fe);
@@ -597,9 +691,9 @@ static void dvb_frontend_stop(struct dvb_frontend *fe)
 {
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
-       fepriv->exit = 1;
+       fepriv->exit = DVB_FE_NORMAL_EXIT;
        mb();
 
        if (!fepriv->thread)
@@ -665,10 +759,10 @@ static int dvb_frontend_start(struct dvb_frontend *fe)
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        struct task_struct *fe_thread;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        if (fepriv->thread) {
-               if (!fepriv->exit)
+               if (fepriv->exit == DVB_FE_NO_EXIT)
                        return 0;
                else
                        dvb_frontend_stop (fe);
@@ -680,12 +774,12 @@ static int dvb_frontend_start(struct dvb_frontend *fe)
                return -EINTR;
 
        fepriv->state = FESTATE_IDLE;
-       fepriv->exit = 0;
+       fepriv->exit = DVB_FE_NO_EXIT;
        fepriv->thread = NULL;
        mb();
 
        fe_thread = kthread_run(dvb_frontend_thread, fe,
-               "kdvb-fe-%i", fe->dvb->num);
+               "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id);
        if (IS_ERR(fe_thread)) {
                ret = PTR_ERR(fe_thread);
                printk("dvb_frontend_start: failed to start kthread (%d)\n", ret);
@@ -709,8 +803,8 @@ static void dvb_frontend_get_frequeny_limits(struct dvb_frontend *fe,
                *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max);
 
        if (*freq_min == 0 || *freq_max == 0)
-               printk(KERN_WARNING "DVB: frontend %u frequency limits undefined - fix the driver\n",
-                      fe->dvb->num);
+               printk(KERN_WARNING "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
+                      fe->dvb->num,fe->id);
 }
 
 static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
@@ -723,8 +817,8 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
        dvb_frontend_get_frequeny_limits(fe, &freq_min, &freq_max);
        if ((freq_min && parms->frequency < freq_min) ||
            (freq_max && parms->frequency > freq_max)) {
-               printk(KERN_WARNING "DVB: frontend %u frequency %u out of range (%u..%u)\n",
-                      fe->dvb->num, parms->frequency, freq_min, freq_max);
+               printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
+                      fe->dvb->num, fe->id, parms->frequency, freq_min, freq_max);
                return -EINVAL;
        }
 
@@ -734,8 +828,8 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
                     parms->u.qpsk.symbol_rate < fe->ops.info.symbol_rate_min) ||
                    (fe->ops.info.symbol_rate_max &&
                     parms->u.qpsk.symbol_rate > fe->ops.info.symbol_rate_max)) {
-                       printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n",
-                              fe->dvb->num, parms->u.qpsk.symbol_rate,
+                       printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
+                              fe->dvb->num, fe->id, parms->u.qpsk.symbol_rate,
                               fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max);
                        return -EINVAL;
                }
@@ -745,16 +839,654 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
                     parms->u.qam.symbol_rate < fe->ops.info.symbol_rate_min) ||
                    (fe->ops.info.symbol_rate_max &&
                     parms->u.qam.symbol_rate > fe->ops.info.symbol_rate_max)) {
-                       printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n",
-                              fe->dvb->num, parms->u.qam.symbol_rate,
+                       printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
+                              fe->dvb->num, fe->id, parms->u.qam.symbol_rate,
                               fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max);
                        return -EINVAL;
                }
        }
 
+       /* check for supported modulation */
+       if (fe->ops.info.type == FE_QAM &&
+           (parms->u.qam.modulation > QAM_AUTO ||
+            !((1 << (parms->u.qam.modulation + 10)) & fe->ops.info.caps))) {
+               printk(KERN_WARNING "DVB: adapter %i frontend %i modulation %u not supported\n",
+                      fe->dvb->num, fe->id, parms->u.qam.modulation);
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
+{
+       int i;
+
+       memset(&(fe->dtv_property_cache), 0,
+                       sizeof(struct dtv_frontend_properties));
+
+       fe->dtv_property_cache.state = DTV_CLEAR;
+       fe->dtv_property_cache.delivery_system = SYS_UNDEFINED;
+       fe->dtv_property_cache.inversion = INVERSION_AUTO;
+       fe->dtv_property_cache.fec_inner = FEC_AUTO;
+       fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO;
+       fe->dtv_property_cache.bandwidth_hz = BANDWIDTH_AUTO;
+       fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO;
+       fe->dtv_property_cache.hierarchy = HIERARCHY_AUTO;
+       fe->dtv_property_cache.symbol_rate = QAM_AUTO;
+       fe->dtv_property_cache.code_rate_HP = FEC_AUTO;
+       fe->dtv_property_cache.code_rate_LP = FEC_AUTO;
+
+       fe->dtv_property_cache.isdbt_partial_reception = -1;
+       fe->dtv_property_cache.isdbt_sb_mode = -1;
+       fe->dtv_property_cache.isdbt_sb_subchannel = -1;
+       fe->dtv_property_cache.isdbt_sb_segment_idx = -1;
+       fe->dtv_property_cache.isdbt_sb_segment_count = -1;
+       fe->dtv_property_cache.isdbt_layer_enabled = 0x7;
+       for (i = 0; i < 3; i++) {
+               fe->dtv_property_cache.layer[i].fec = FEC_AUTO;
+               fe->dtv_property_cache.layer[i].modulation = QAM_AUTO;
+               fe->dtv_property_cache.layer[i].interleaving = -1;
+               fe->dtv_property_cache.layer[i].segment_count = -1;
+       }
+
        return 0;
 }
 
+#define _DTV_CMD(n, s, b) \
+[n] = { \
+       .name = #n, \
+       .cmd  = n, \
+       .set  = s,\
+       .buffer = b \
+}
+
+static struct dtv_cmds_h dtv_cmds[] = {
+       _DTV_CMD(DTV_TUNE, 1, 0),
+       _DTV_CMD(DTV_CLEAR, 1, 0),
+
+       /* Set */
+       _DTV_CMD(DTV_FREQUENCY, 1, 0),
+       _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
+       _DTV_CMD(DTV_MODULATION, 1, 0),
+       _DTV_CMD(DTV_INVERSION, 1, 0),
+       _DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
+       _DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
+       _DTV_CMD(DTV_INNER_FEC, 1, 0),
+       _DTV_CMD(DTV_VOLTAGE, 1, 0),
+       _DTV_CMD(DTV_TONE, 1, 0),
+       _DTV_CMD(DTV_PILOT, 1, 0),
+       _DTV_CMD(DTV_ROLLOFF, 1, 0),
+       _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0),
+       _DTV_CMD(DTV_HIERARCHY, 1, 0),
+       _DTV_CMD(DTV_CODE_RATE_HP, 1, 0),
+       _DTV_CMD(DTV_CODE_RATE_LP, 1, 0),
+       _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0),
+       _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0),
+
+       _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
+       _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
+       _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0),
+       _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0),
+       _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0),
+
+       _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 0, 0),
+       _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 0, 0),
+       _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 0, 0),
+       _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 0, 0),
+       _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 0, 0),
+       _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0),
+
+       _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
+
+       /* Get */
+       _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
+       _DTV_CMD(DTV_API_VERSION, 0, 0),
+       _DTV_CMD(DTV_CODE_RATE_HP, 0, 0),
+       _DTV_CMD(DTV_CODE_RATE_LP, 0, 0),
+       _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0),
+       _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0),
+       _DTV_CMD(DTV_HIERARCHY, 0, 0),
+};
+
+static void dtv_property_dump(struct dtv_property *tvp)
+{
+       int i;
+
+       if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) {
+               printk(KERN_WARNING "%s: tvp.cmd = 0x%08x undefined\n",
+                       __func__, tvp->cmd);
+               return;
+       }
+
+       dprintk("%s() tvp.cmd    = 0x%08x (%s)\n"
+               ,__func__
+               ,tvp->cmd
+               ,dtv_cmds[ tvp->cmd ].name);
+
+       if(dtv_cmds[ tvp->cmd ].buffer) {
+
+               dprintk("%s() tvp.u.buffer.len = 0x%02x\n"
+                       ,__func__
+                       ,tvp->u.buffer.len);
+
+               for(i = 0; i < tvp->u.buffer.len; i++)
+                       dprintk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n"
+                               ,__func__
+                               ,i
+                               ,tvp->u.buffer.data[i]);
+
+       } else
+               dprintk("%s() tvp.u.data = 0x%08x\n", __func__, tvp->u.data);
+}
+
+static 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) ||
+          (s == SYS_ATSC))
+               return 1;
+
+       return 0;
+}
+
+/* Synchronise the legacy tuning parameters into the cache, so that demodulator
+ * drivers can use a single set_frontend tuning function, regardless of whether
+ * it's being used for the legacy or new API, reducing code and complexity.
+ */
+static void dtv_property_cache_sync(struct dvb_frontend *fe,
+                                   struct dvb_frontend_parameters *p)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+       c->frequency = p->frequency;
+       c->inversion = p->inversion;
+
+       switch (fe->ops.info.type) {
+       case FE_QPSK:
+               c->modulation = QPSK;   /* implied for DVB-S in legacy API */
+               c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+               c->symbol_rate = p->u.qpsk.symbol_rate;
+               c->fec_inner = p->u.qpsk.fec_inner;
+               c->delivery_system = SYS_DVBS;
+               break;
+       case FE_QAM:
+               c->symbol_rate = p->u.qam.symbol_rate;
+               c->fec_inner = p->u.qam.fec_inner;
+               c->modulation = p->u.qam.modulation;
+               c->delivery_system = SYS_DVBC_ANNEX_AC;
+               break;
+       case FE_OFDM:
+               if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+                       c->bandwidth_hz = 6000000;
+               else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+                       c->bandwidth_hz = 7000000;
+               else if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ)
+                       c->bandwidth_hz = 8000000;
+               else
+                       /* Including BANDWIDTH_AUTO */
+                       c->bandwidth_hz = 0;
+               c->code_rate_HP = p->u.ofdm.code_rate_HP;
+               c->code_rate_LP = p->u.ofdm.code_rate_LP;
+               c->modulation = p->u.ofdm.constellation;
+               c->transmission_mode = p->u.ofdm.transmission_mode;
+               c->guard_interval = p->u.ofdm.guard_interval;
+               c->hierarchy = p->u.ofdm.hierarchy_information;
+               c->delivery_system = SYS_DVBT;
+               break;
+       case FE_ATSC:
+               c->modulation = p->u.vsb.modulation;
+               if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+                       c->delivery_system = SYS_ATSC;
+               else
+                       c->delivery_system = SYS_DVBC_ANNEX_B;
+               break;
+       }
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the advanced tuning API.
+ */
+static void dtv_property_legacy_params_sync(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+       p->frequency = c->frequency;
+       p->inversion = c->inversion;
+
+       switch (fe->ops.info.type) {
+       case FE_QPSK:
+               dprintk("%s() Preparing QPSK req\n", __func__);
+               p->u.qpsk.symbol_rate = c->symbol_rate;
+               p->u.qpsk.fec_inner = c->fec_inner;
+               c->delivery_system = SYS_DVBS;
+               break;
+       case FE_QAM:
+               dprintk("%s() Preparing QAM req\n", __func__);
+               p->u.qam.symbol_rate = c->symbol_rate;
+               p->u.qam.fec_inner = c->fec_inner;
+               p->u.qam.modulation = c->modulation;
+               c->delivery_system = SYS_DVBC_ANNEX_AC;
+               break;
+       case FE_OFDM:
+               dprintk("%s() Preparing OFDM req\n", __func__);
+               if (c->bandwidth_hz == 6000000)
+                       p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+               else if (c->bandwidth_hz == 7000000)
+                       p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+               else if (c->bandwidth_hz == 8000000)
+                       p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+               else
+                       p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+               p->u.ofdm.code_rate_HP = c->code_rate_HP;
+               p->u.ofdm.code_rate_LP = c->code_rate_LP;
+               p->u.ofdm.constellation = c->modulation;
+               p->u.ofdm.transmission_mode = c->transmission_mode;
+               p->u.ofdm.guard_interval = c->guard_interval;
+               p->u.ofdm.hierarchy_information = c->hierarchy;
+               c->delivery_system = SYS_DVBT;
+               break;
+       case FE_ATSC:
+               dprintk("%s() Preparing VSB req\n", __func__);
+               p->u.vsb.modulation = c->modulation;
+               if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+                       c->delivery_system = SYS_ATSC;
+               else
+                       c->delivery_system = SYS_DVBC_ANNEX_B;
+               break;
+       }
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the legacy tuning API.
+ */
+static void dtv_property_adv_params_sync(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+       p->frequency = c->frequency;
+       p->inversion = c->inversion;
+
+       switch(c->modulation) {
+       case PSK_8:
+       case APSK_16:
+       case APSK_32:
+       case QPSK:
+               p->u.qpsk.symbol_rate = c->symbol_rate;
+               p->u.qpsk.fec_inner = c->fec_inner;
+               break;
+       default:
+               break;
+       }
+
+       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 = c->inversion;
+               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.transmission_mode = TRANSMISSION_MODE_AUTO;
+               p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+               p->u.ofdm.hierarchy_information = HIERARCHY_AUTO;
+               if (c->bandwidth_hz == 8000000)
+                       p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+               else if (c->bandwidth_hz == 7000000)
+                       p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+               else if (c->bandwidth_hz == 6000000)
+                       p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+               else
+                       p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+       }
+}
+
+static void dtv_property_cache_submit(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+       /* For legacy delivery systems we don't need the delivery_system to
+        * be specified, but we populate the older structures from the cache
+        * so we can call set_frontend on older drivers.
+        */
+       if(is_legacy_delivery_system(c->delivery_system)) {
+
+               dprintk("%s() legacy, modulation = %d\n", __func__, c->modulation);
+               dtv_property_legacy_params_sync(fe);
+
+       } else {
+               dprintk("%s() adv, modulation = %d\n", __func__, c->modulation);
+
+               /* 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.
+                */
+               dtv_property_adv_params_sync(fe);
+       }
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+                       unsigned int cmd, void *parg);
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+                       unsigned int cmd, void *parg);
+
+static int dtv_property_process_get(struct dvb_frontend *fe,
+                                   struct dtv_property *tvp,
+                                   struct inode *inode, struct file *file)
+{
+       int r = 0;
+
+       /* Allow the frontend to validate incoming properties */
+       if (fe->ops.get_property)
+               r = fe->ops.get_property(fe, tvp);
+
+       if (r < 0)
+               return r;
+
+       switch(tvp->cmd) {
+       case DTV_FREQUENCY:
+               tvp->u.data = fe->dtv_property_cache.frequency;
+               break;
+       case DTV_MODULATION:
+               tvp->u.data = fe->dtv_property_cache.modulation;
+               break;
+       case DTV_BANDWIDTH_HZ:
+               tvp->u.data = fe->dtv_property_cache.bandwidth_hz;
+               break;
+       case DTV_INVERSION:
+               tvp->u.data = fe->dtv_property_cache.inversion;
+               break;
+       case DTV_SYMBOL_RATE:
+               tvp->u.data = fe->dtv_property_cache.symbol_rate;
+               break;
+       case DTV_INNER_FEC:
+               tvp->u.data = fe->dtv_property_cache.fec_inner;
+               break;
+       case DTV_PILOT:
+               tvp->u.data = fe->dtv_property_cache.pilot;
+               break;
+       case DTV_ROLLOFF:
+               tvp->u.data = fe->dtv_property_cache.rolloff;
+               break;
+       case DTV_DELIVERY_SYSTEM:
+               tvp->u.data = fe->dtv_property_cache.delivery_system;
+               break;
+       case DTV_VOLTAGE:
+               tvp->u.data = fe->dtv_property_cache.voltage;
+               break;
+       case DTV_TONE:
+               tvp->u.data = fe->dtv_property_cache.sectone;
+               break;
+       case DTV_API_VERSION:
+               tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
+               break;
+       case DTV_CODE_RATE_HP:
+               tvp->u.data = fe->dtv_property_cache.code_rate_HP;
+               break;
+       case DTV_CODE_RATE_LP:
+               tvp->u.data = fe->dtv_property_cache.code_rate_LP;
+               break;
+       case DTV_GUARD_INTERVAL:
+               tvp->u.data = fe->dtv_property_cache.guard_interval;
+               break;
+       case DTV_TRANSMISSION_MODE:
+               tvp->u.data = fe->dtv_property_cache.transmission_mode;
+               break;
+       case DTV_HIERARCHY:
+               tvp->u.data = fe->dtv_property_cache.hierarchy;
+               break;
+
+       /* ISDB-T Support here */
+       case DTV_ISDBT_PARTIAL_RECEPTION:
+               tvp->u.data = fe->dtv_property_cache.isdbt_partial_reception;
+               break;
+       case DTV_ISDBT_SOUND_BROADCASTING:
+               tvp->u.data = fe->dtv_property_cache.isdbt_sb_mode;
+               break;
+       case DTV_ISDBT_SB_SUBCHANNEL_ID:
+               tvp->u.data = fe->dtv_property_cache.isdbt_sb_subchannel;
+               break;
+       case DTV_ISDBT_SB_SEGMENT_IDX:
+               tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_idx;
+               break;
+       case DTV_ISDBT_SB_SEGMENT_COUNT:
+               tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_count;
+               break;
+       case DTV_ISDBT_LAYER_ENABLED:
+               tvp->u.data = fe->dtv_property_cache.isdbt_layer_enabled;
+               break;
+       case DTV_ISDBT_LAYERA_FEC:
+               tvp->u.data = fe->dtv_property_cache.layer[0].fec;
+               break;
+       case DTV_ISDBT_LAYERA_MODULATION:
+               tvp->u.data = fe->dtv_property_cache.layer[0].modulation;
+               break;
+       case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+               tvp->u.data = fe->dtv_property_cache.layer[0].segment_count;
+               break;
+       case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+               tvp->u.data = fe->dtv_property_cache.layer[0].interleaving;
+               break;
+       case DTV_ISDBT_LAYERB_FEC:
+               tvp->u.data = fe->dtv_property_cache.layer[1].fec;
+               break;
+       case DTV_ISDBT_LAYERB_MODULATION:
+               tvp->u.data = fe->dtv_property_cache.layer[1].modulation;
+               break;
+       case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+               tvp->u.data = fe->dtv_property_cache.layer[1].segment_count;
+               break;
+       case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+               tvp->u.data = fe->dtv_property_cache.layer[1].interleaving;
+               break;
+       case DTV_ISDBT_LAYERC_FEC:
+               tvp->u.data = fe->dtv_property_cache.layer[2].fec;
+               break;
+       case DTV_ISDBT_LAYERC_MODULATION:
+               tvp->u.data = fe->dtv_property_cache.layer[2].modulation;
+               break;
+       case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+               tvp->u.data = fe->dtv_property_cache.layer[2].segment_count;
+               break;
+       case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+               tvp->u.data = fe->dtv_property_cache.layer[2].interleaving;
+               break;
+       case DTV_ISDBS_TS_ID:
+               tvp->u.data = fe->dtv_property_cache.isdbs_ts_id;
+               break;
+       default:
+               r = -1;
+       }
+
+       dtv_property_dump(tvp);
+
+       return r;
+}
+
+static int dtv_property_process_set(struct dvb_frontend *fe,
+                                   struct dtv_property *tvp,
+                                   struct inode *inode,
+                                   struct file *file)
+{
+       int r = 0;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       dtv_property_dump(tvp);
+
+       /* Allow the frontend to validate incoming properties */
+       if (fe->ops.set_property)
+               r = fe->ops.set_property(fe, tvp);
+
+       if (r < 0)
+               return r;
+
+       switch(tvp->cmd) {
+       case DTV_CLEAR:
+               /* Reset a cache of data specific to the frontend here. This does
+                * not effect hardware.
+                */
+               dvb_frontend_clear_cache(fe);
+               dprintk("%s() Flushing property cache\n", __func__);
+               break;
+       case DTV_TUNE:
+               /* interpret the cache of data, build either a traditional frontend
+                * tunerequest so we can pass validation in the FE_SET_FRONTEND
+                * ioctl.
+                */
+               fe->dtv_property_cache.state = tvp->cmd;
+               dprintk("%s() Finalised property cache\n", __func__);
+               dtv_property_cache_submit(fe);
+
+               r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND,
+                       &fepriv->parameters);
+               break;
+       case DTV_FREQUENCY:
+               fe->dtv_property_cache.frequency = tvp->u.data;
+               break;
+       case DTV_MODULATION:
+               fe->dtv_property_cache.modulation = tvp->u.data;
+               break;
+       case DTV_BANDWIDTH_HZ:
+               fe->dtv_property_cache.bandwidth_hz = tvp->u.data;
+               break;
+       case DTV_INVERSION:
+               fe->dtv_property_cache.inversion = tvp->u.data;
+               break;
+       case DTV_SYMBOL_RATE:
+               fe->dtv_property_cache.symbol_rate = tvp->u.data;
+               break;
+       case DTV_INNER_FEC:
+               fe->dtv_property_cache.fec_inner = tvp->u.data;
+               break;
+       case DTV_PILOT:
+               fe->dtv_property_cache.pilot = tvp->u.data;
+               break;
+       case DTV_ROLLOFF:
+               fe->dtv_property_cache.rolloff = tvp->u.data;
+               break;
+       case DTV_DELIVERY_SYSTEM:
+               fe->dtv_property_cache.delivery_system = tvp->u.data;
+               break;
+       case DTV_VOLTAGE:
+               fe->dtv_property_cache.voltage = tvp->u.data;
+               r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE,
+                       (void *)fe->dtv_property_cache.voltage);
+               break;
+       case DTV_TONE:
+               fe->dtv_property_cache.sectone = tvp->u.data;
+               r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE,
+                       (void *)fe->dtv_property_cache.sectone);
+               break;
+       case DTV_CODE_RATE_HP:
+               fe->dtv_property_cache.code_rate_HP = tvp->u.data;
+               break;
+       case DTV_CODE_RATE_LP:
+               fe->dtv_property_cache.code_rate_LP = tvp->u.data;
+               break;
+       case DTV_GUARD_INTERVAL:
+               fe->dtv_property_cache.guard_interval = tvp->u.data;
+               break;
+       case DTV_TRANSMISSION_MODE:
+               fe->dtv_property_cache.transmission_mode = tvp->u.data;
+               break;
+       case DTV_HIERARCHY:
+               fe->dtv_property_cache.hierarchy = tvp->u.data;
+               break;
+
+       /* ISDB-T Support here */
+       case DTV_ISDBT_PARTIAL_RECEPTION:
+               fe->dtv_property_cache.isdbt_partial_reception = tvp->u.data;
+               break;
+       case DTV_ISDBT_SOUND_BROADCASTING:
+               fe->dtv_property_cache.isdbt_sb_mode = tvp->u.data;
+               break;
+       case DTV_ISDBT_SB_SUBCHANNEL_ID:
+               fe->dtv_property_cache.isdbt_sb_subchannel = tvp->u.data;
+               break;
+       case DTV_ISDBT_SB_SEGMENT_IDX:
+               fe->dtv_property_cache.isdbt_sb_segment_idx = tvp->u.data;
+               break;
+       case DTV_ISDBT_SB_SEGMENT_COUNT:
+               fe->dtv_property_cache.isdbt_sb_segment_count = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYER_ENABLED:
+               fe->dtv_property_cache.isdbt_layer_enabled = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERA_FEC:
+               fe->dtv_property_cache.layer[0].fec = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERA_MODULATION:
+               fe->dtv_property_cache.layer[0].modulation = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+               fe->dtv_property_cache.layer[0].segment_count = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+               fe->dtv_property_cache.layer[0].interleaving = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERB_FEC:
+               fe->dtv_property_cache.layer[1].fec = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERB_MODULATION:
+               fe->dtv_property_cache.layer[1].modulation = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+               fe->dtv_property_cache.layer[1].segment_count = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+               fe->dtv_property_cache.layer[1].interleaving = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERC_FEC:
+               fe->dtv_property_cache.layer[2].fec = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERC_MODULATION:
+               fe->dtv_property_cache.layer[2].modulation = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+               fe->dtv_property_cache.layer[2].segment_count = tvp->u.data;
+               break;
+       case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+               fe->dtv_property_cache.layer[2].interleaving = tvp->u.data;
+               break;
+       case DTV_ISDBS_TS_ID:
+               fe->dtv_property_cache.isdbs_ts_id = tvp->u.data;
+               break;
+       default:
+               r = -1;
+       }
+
+       return r;
+}
+
 static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
                        unsigned int cmd, void *parg)
 {
@@ -763,9 +1495,9 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        int err = -EOPNOTSUPP;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk("%s (%d)\n", __func__, _IOC_NR(cmd));
 
-       if (fepriv->exit)
+       if (fepriv->exit != DVB_FE_NO_EXIT)
                return -ENODEV;
 
        if ((file->f_flags & O_ACCMODE) == O_RDONLY &&
@@ -776,6 +1508,121 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
        if (down_interruptible (&fepriv->sem))
                return -ERESTARTSYS;
 
+       if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY))
+               err = dvb_frontend_ioctl_properties(inode, file, cmd, parg);
+       else {
+               fe->dtv_property_cache.state = DTV_UNDEFINED;
+               err = dvb_frontend_ioctl_legacy(inode, file, cmd, parg);
+       }
+
+       up(&fepriv->sem);
+       return err;
+}
+
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+                       unsigned int cmd, void *parg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_frontend *fe = dvbdev->priv;
+       int err = 0;
+
+       struct dtv_properties *tvps = NULL;
+       struct dtv_property *tvp = NULL;
+       int i;
+
+       dprintk("%s\n", __func__);
+
+       if(cmd == FE_SET_PROPERTY) {
+               tvps = (struct dtv_properties __user *)parg;
+
+               dprintk("%s() properties.num = %d\n", __func__, tvps->num);
+               dprintk("%s() properties.props = %p\n", __func__, tvps->props);
+
+               /* Put an arbitrary limit on the number of messages that can
+                * be sent at once */
+               if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+                       return -EINVAL;
+
+               tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
+               if (!tvp) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+
+               if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               for (i = 0; i < tvps->num; i++) {
+                       (tvp + i)->result = dtv_property_process_set(fe, tvp + i, inode, file);
+                       err |= (tvp + i)->result;
+               }
+
+               if(fe->dtv_property_cache.state == DTV_TUNE)
+                       dprintk("%s() Property cache is full, tuning\n", __func__);
+
+       } else
+       if(cmd == FE_GET_PROPERTY) {
+
+               tvps = (struct dtv_properties __user *)parg;
+
+               dprintk("%s() properties.num = %d\n", __func__, tvps->num);
+               dprintk("%s() properties.props = %p\n", __func__, tvps->props);
+
+               /* Put an arbitrary limit on the number of messages that can
+                * be sent at once */
+               if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+                       return -EINVAL;
+
+               tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
+               if (!tvp) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+
+               if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               for (i = 0; i < tvps->num; i++) {
+                       (tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file);
+                       err |= (tvp + i)->result;
+               }
+
+               if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+       } else
+               err = -EOPNOTSUPP;
+
+out:
+       kfree(tvp);
+       return err;
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+                       unsigned int cmd, void *parg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_frontend *fe = dvbdev->priv;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       int cb_err, err = -EOPNOTSUPP;
+
+       if (fe->dvb->fe_ioctl_override) {
+               cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
+                                                   DVB_FE_IOCTL_PRE);
+               if (cb_err < 0)
+                       return cb_err;
+               if (cb_err > 0)
+                       return 0;
+               /* fe_ioctl_override returning 0 allows
+                * dvb-core to continue handling the ioctl */
+       }
+
        switch (cmd) {
        case FE_GET_INFO: {
                struct dvb_frontend_info* info = parg;
@@ -794,7 +1641,8 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
 
                /* if retune was requested but hasn't occured yet, prevent
                 * that user get signal state from previous tuning */
-               if(fepriv->state == FESTATE_RETUNE) {
+               if (fepriv->state == FESTATE_RETUNE ||
+                   fepriv->state == FESTATE_ERROR) {
                        err=0;
                        *status = 0;
                        break;
@@ -889,13 +1737,13 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
                         * initialization, so parg is 8 bits and does not
                         * include the initialization or start bit
                         */
-                       unsigned long cmd = ((unsigned long) parg) << 1;
+                       unsigned long swcmd = ((unsigned long) parg) << 1;
                        struct timeval nexttime;
                        struct timeval tv[10];
                        int i;
                        u8 last = 1;
                        if (dvb_frontend_debug)
-                               printk("%s switch command: 0x%04lx\n", __FUNCTION__, cmd);
+                               printk("%s switch command: 0x%04lx\n", __func__, swcmd);
                        do_gettimeofday(&nexttime);
                        if (dvb_frontend_debug)
                                memcpy(&tv[0], &nexttime, sizeof(struct timeval));
@@ -908,18 +1756,18 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
                        for (i = 0; i < 9; i++) {
                                if (dvb_frontend_debug)
                                        do_gettimeofday(&tv[i + 1]);
-                               if ((cmd & 0x01) != last) {
+                               if ((swcmd & 0x01) != last) {
                                        /* set voltage to (last ? 13V : 18V) */
                                        fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
                                        last = (last) ? 0 : 1;
                                }
-                               cmd = cmd >> 1;
+                               swcmd = swcmd >> 1;
                                if (i != 8)
                                        dvb_frontend_sleep_until(&nexttime, 8000);
                        }
                        if (dvb_frontend_debug) {
                                printk("%s(%d): switch delay (should be 32k followed by all 8k\n",
-                                       __FUNCTION__, fe->dvb->num);
+                                       __func__, fe->dvb->num);
                                for (i = 1; i < 10; i++)
                                        printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i]));
                        }
@@ -942,13 +1790,21 @@ 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->dtv_property_cache.state == DTV_TUNE) {
+                       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));
+                       dtv_property_cache_sync(fe, &fepriv->parameters);
+               }
 
                memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
                memcpy(&fetunesettings.parameters, parg,
@@ -1003,6 +1859,10 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
                        fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
 
                fepriv->state = FESTATE_RETUNE;
+
+               /* Request the search algorithm to search */
+               fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+
                dvb_frontend_wakeup(fe);
                dvb_frontend_add_event(fe, 0);
                fepriv->status = 0;
@@ -1027,17 +1887,24 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
                break;
        };
 
-       up (&fepriv->sem);
+       if (fe->dvb->fe_ioctl_override) {
+               cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
+                                                   DVB_FE_IOCTL_POST);
+               if (cb_err < 0)
+                       return cb_err;
+       }
+
        return err;
 }
 
+
 static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
 {
        struct dvb_device *dvbdev = file->private_data;
        struct dvb_frontend *fe = dvbdev->priv;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        poll_wait (file, &fepriv->events.wait_queue, wait);
 
@@ -1052,13 +1919,55 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
        struct dvb_device *dvbdev = file->private_data;
        struct dvb_frontend *fe = dvbdev->priv;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       struct dvb_adapter *adapter = fe->dvb;
        int ret;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
+       if (fepriv->exit == DVB_FE_DEVICE_REMOVED)
+               return -ENODEV;
+
+       if (adapter->mfe_shared) {
+               mutex_lock (&adapter->mfe_lock);
+
+               if (adapter->mfe_dvbdev == NULL)
+                       adapter->mfe_dvbdev = dvbdev;
+
+               else if (adapter->mfe_dvbdev != dvbdev) {
+                       struct dvb_device
+                               *mfedev = adapter->mfe_dvbdev;
+                       struct dvb_frontend
+                               *mfe = mfedev->priv;
+                       struct dvb_frontend_private
+                               *mfepriv = mfe->frontend_priv;
+                       int mferetry = (dvb_mfe_wait_time << 1);
+
+                       mutex_unlock (&adapter->mfe_lock);
+                       while (mferetry-- && (mfedev->users != -1 ||
+                                       mfepriv->thread != NULL)) {
+                               if(msleep_interruptible(500)) {
+                                       if(signal_pending(current))
+                                               return -EINTR;
+                               }
+                       }
+
+                       mutex_lock (&adapter->mfe_lock);
+                       if(adapter->mfe_dvbdev != dvbdev) {
+                               mfedev = adapter->mfe_dvbdev;
+                               mfe = mfedev->priv;
+                               mfepriv = mfe->frontend_priv;
+                               if (mfedev->users != -1 ||
+                                               mfepriv->thread != NULL) {
+                                       mutex_unlock (&adapter->mfe_lock);
+                                       return -EBUSY;
+                               }
+                               adapter->mfe_dvbdev = dvbdev;
+                       }
+               }
+       }
 
        if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
                if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
-                       return ret;
+                       goto err0;
        }
 
        if ((ret = dvb_generic_open (inode, file)) < 0)
@@ -1078,6 +1987,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
                fepriv->events.eventr = fepriv->events.eventw = 0;
        }
 
+       if (adapter->mfe_shared)
+               mutex_unlock (&adapter->mfe_lock);
        return ret;
 
 err2:
@@ -1085,6 +1996,9 @@ err2:
 err1:
        if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
                fe->ops.ts_bus_ctrl(fe, 0);
+err0:
+       if (adapter->mfe_shared)
+               mutex_unlock (&adapter->mfe_lock);
        return ret;
 }
 
@@ -1095,7 +2009,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        int ret;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        if ((file->f_flags & O_ACCMODE) != O_RDONLY)
                fepriv->release_jiffies = jiffies;
@@ -1103,7 +2017,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
        ret = dvb_generic_release (inode, file);
 
        if (dvbdev->users == -1) {
-               if (fepriv->exit == 1) {
+               if (fepriv->exit != DVB_FE_NO_EXIT) {
                        fops_put(file->f_op);
                        file->f_op = NULL;
                        wake_up(&dvbdev->wait_queue);
@@ -1115,7 +2029,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
        return ret;
 }
 
-static struct file_operations dvb_frontend_fops = {
+static const struct file_operations dvb_frontend_fops = {
        .owner          = THIS_MODULE,
        .ioctl          = dvb_generic_ioctl,
        .poll           = dvb_frontend_poll,
@@ -1135,7 +2049,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
                .kernel_ioctl = dvb_frontend_ioctl
        };
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        if (mutex_lock_interruptible(&frontend_mutex))
                return -ERESTARTSYS;
@@ -1154,8 +2068,9 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
        fe->dvb = dvb;
        fepriv->inversion = INVERSION_OFF;
 
-       printk ("DVB: registering frontend %i (%s)...\n",
+       printk ("DVB: registering adapter %i frontend %i (%s)...\n",
                fe->dvb->num,
+               fe->id,
                fe->ops.info.name);
 
        dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
@@ -1169,7 +2084,7 @@ EXPORT_SYMBOL(dvb_register_frontend);
 int dvb_unregister_frontend(struct dvb_frontend* fe)
 {
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        mutex_lock(&frontend_mutex);
        dvb_frontend_stop (fe);
@@ -1189,7 +2104,7 @@ int dvb_unregister_frontend(struct dvb_frontend* fe)
 }
 EXPORT_SYMBOL(dvb_unregister_frontend);
 
-#ifdef CONFIG_DVB_CORE_ATTACH
+#ifdef CONFIG_MEDIA_ATTACH
 void dvb_frontend_detach(struct dvb_frontend* fe)
 {
        void *ptr;