V4L/DVB (9005): Bug fix: ioctl FE_SET_PROPERTY/FE_GET_PROPERTY always return error
[safe/jmp/linux-2.6] / drivers / media / dvb / dvb-core / dvb_frontend.c
index 7c42d53..161ebcf 100644 (file)
 #include <linux/slab.h>
 #include <linux/poll.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/list.h>
 #include <linux/freezer.h>
 #include <linux/jiffies.h>
+#include <linux/kthread.h>
 #include <asm/processor.h>
 
 #include "dvb_frontend.h"
 #include "dvbdev.h"
 
 static int dvb_frontend_debug;
-static int dvb_shutdown_timeout = 5;
+static int dvb_shutdown_timeout;
 static int dvb_force_auto_inversion;
 static int dvb_override_tune_delay;
 static int dvb_powerdown_on_sleep = 1;
@@ -100,7 +100,7 @@ struct dvb_frontend_private {
        struct semaphore sem;
        struct list_head list_head;
        wait_queue_head_t wait_queue;
-       pid_t thread_pid;
+       struct task_struct *thread;
        unsigned long release_jiffies;
        unsigned int exit;
        unsigned int wakeup;
@@ -135,9 +135,9 @@ 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 (down_interruptible (&events->sem))
+       if (mutex_lock_interruptible (&events->mtx))
                return;
 
        wp = (events->eventw + 1) % MAX_EVENT;
@@ -158,7 +158,7 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
 
        events->eventw = wp;
 
-       up (&events->sem);
+       mutex_unlock(&events->mtx);
 
        e->status = status;
 
@@ -171,7 +171,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;
@@ -196,7 +196,7 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
                        return ret;
        }
 
-       if (down_interruptible (&events->sem))
+       if (mutex_lock_interruptible (&events->mtx))
                return -ERESTARTSYS;
 
        memcpy (event, &events->events[events->eventr],
@@ -204,7 +204,7 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
 
        events->eventr = (events->eventr + 1) % MAX_EVENT;
 
-       up (&events->sem);
+       mutex_unlock(&events->mtx);
 
        return 0;
 }
@@ -237,7 +237,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;
@@ -329,7 +329,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 */
@@ -508,18 +508,10 @@ static int dvb_frontend_thread(void *data)
        struct dvb_frontend *fe = data;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        unsigned long timeout;
-       char name [15];
        fe_status_t s;
        struct dvb_frontend_parameters *params;
 
-       dprintk("%s\n", __FUNCTION__);
-
-       snprintf (name, sizeof(name), "kdvb-fe-%i", fe->dvb->num);
-
-       lock_kernel();
-       daemonize(name);
-       sigfillset(&current->blocked);
-       unlock_kernel();
+       dprintk("%s\n", __func__);
 
        fepriv->check_wrapped = 0;
        fepriv->quality = 0;
@@ -530,18 +522,22 @@ static int dvb_frontend_thread(void *data)
 
        dvb_frontend_init(fe);
 
+       set_freezable();
        while (1) {
                up(&fepriv->sem);           /* is locked when we enter the thread... */
-
+restart:
                timeout = wait_event_interruptible_timeout(fepriv->wait_queue,
-                                                          dvb_frontend_should_wakeup(fe),
-                                                          fepriv->delay);
-               if (0 != dvb_frontend_is_exiting(fe)) {
+                       dvb_frontend_should_wakeup(fe) || kthread_should_stop()
+                               || freezing(current),
+                       fepriv->delay);
+
+               if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
                        /* got signal or quitting */
                        break;
                }
 
-               try_to_freeze();
+               if (try_to_freeze())
+                       goto restart;
 
                if (down_interruptible(&fepriv->sem))
                        break;
@@ -578,10 +574,9 @@ static int dvb_frontend_thread(void *data)
                        dvb_frontend_swzigzag(fe);
        }
 
-       if (dvb_shutdown_timeout) {
-               if (dvb_powerdown_on_sleep)
-                       if (fe->ops.set_voltage)
-                               fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
+       if (dvb_powerdown_on_sleep) {
+               if (fe->ops.set_voltage)
+                       fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
                if (fe->ops.tuner_ops.sleep) {
                        fe->ops.tuner_ops.sleep(fe);
                        if (fe->ops.i2c_gate_ctrl)
@@ -591,7 +586,7 @@ static int dvb_frontend_thread(void *data)
                        fe->ops.sleep(fe);
        }
 
-       fepriv->thread_pid = 0;
+       fepriv->thread = NULL;
        mb();
 
        dvb_frontend_wakeup(fe);
@@ -600,41 +595,25 @@ static int dvb_frontend_thread(void *data)
 
 static void dvb_frontend_stop(struct dvb_frontend *fe)
 {
-       unsigned long ret;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        fepriv->exit = 1;
        mb();
 
-       if (!fepriv->thread_pid)
+       if (!fepriv->thread)
                return;
 
-       /* check if the thread is really alive */
-       if (kill_proc(fepriv->thread_pid, 0, 1) == -ESRCH) {
-               printk("dvb_frontend_stop: thread PID %d already died\n",
-                               fepriv->thread_pid);
-               /* make sure the mutex was not held by the thread */
-               init_MUTEX (&fepriv->sem);
-               return;
-       }
-
-       /* wake up the frontend thread, so it notices that fe->exit == 1 */
-       dvb_frontend_wakeup(fe);
+       kthread_stop(fepriv->thread);
 
-       /* wait until the frontend thread has exited */
-       ret = wait_event_interruptible(fepriv->wait_queue,0 == fepriv->thread_pid);
-       if (-ERESTARTSYS != ret) {
-               fepriv->state = FESTATE_IDLE;
-               return;
-       }
+       init_MUTEX (&fepriv->sem);
        fepriv->state = FESTATE_IDLE;
 
        /* paranoia check in case a signal arrived */
-       if (fepriv->thread_pid)
-               printk("dvb_frontend_stop: warning: thread PID %d won't exit\n",
-                               fepriv->thread_pid);
+       if (fepriv->thread)
+               printk("dvb_frontend_stop: warning: thread %p won't exit\n",
+                               fepriv->thread);
 }
 
 s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime)
@@ -684,10 +663,11 @@ static int dvb_frontend_start(struct dvb_frontend *fe)
 {
        int ret;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       struct task_struct *fe_thread;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
-       if (fepriv->thread_pid) {
+       if (fepriv->thread) {
                if (!fepriv->exit)
                        return 0;
                else
@@ -701,21 +681,601 @@ static int dvb_frontend_start(struct dvb_frontend *fe)
 
        fepriv->state = FESTATE_IDLE;
        fepriv->exit = 0;
-       fepriv->thread_pid = 0;
+       fepriv->thread = NULL;
        mb();
 
-       ret = kernel_thread (dvb_frontend_thread, fe, 0);
-
-       if (ret < 0) {
-               printk("dvb_frontend_start: failed to start kernel_thread (%d)\n", ret);
+       fe_thread = kthread_run(dvb_frontend_thread, fe,
+               "kdvb-fe-%i", fe->dvb->num);
+       if (IS_ERR(fe_thread)) {
+               ret = PTR_ERR(fe_thread);
+               printk("dvb_frontend_start: failed to start kthread (%d)\n", ret);
                up(&fepriv->sem);
                return ret;
        }
-       fepriv->thread_pid = ret;
+       fepriv->thread = fe_thread;
+       return 0;
+}
+
+static void dvb_frontend_get_frequeny_limits(struct dvb_frontend *fe,
+                                       u32 *freq_min, u32 *freq_max)
+{
+       *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min);
+
+       if (fe->ops.info.frequency_max == 0)
+               *freq_max = fe->ops.tuner_ops.info.frequency_max;
+       else if (fe->ops.tuner_ops.info.frequency_max == 0)
+               *freq_max = fe->ops.info.frequency_max;
+       else
+               *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);
+}
+
+static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
+                               struct dvb_frontend_parameters *parms)
+{
+       u32 freq_min;
+       u32 freq_max;
+
+       /* range check: frequency */
+       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);
+               return -EINVAL;
+       }
+
+       /* range check: symbol rate */
+       if (fe->ops.info.type == FE_QPSK) {
+               if ((fe->ops.info.symbol_rate_min &&
+                    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,
+                              fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max);
+                       return -EINVAL;
+               }
+
+       } else if (fe->ops.info.type == FE_QAM) {
+               if ((fe->ops.info.symbol_rate_min &&
+                    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,
+                              fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max);
+                       return -EINVAL;
+               }
+       }
 
        return 0;
 }
 
+struct dtv_cmds_h dtv_cmds[] = {
+       [DTV_TUNE] = {
+               .name   = "DTV_TUNE",
+               .cmd    = DTV_TUNE,
+               .set    = 1,
+       },
+       [DTV_CLEAR] = {
+               .name   = "DTV_CLEAR",
+               .cmd    = DTV_CLEAR,
+               .set    = 1,
+       },
+
+       /* Set */
+       [DTV_FREQUENCY] = {
+               .name   = "DTV_FREQUENCY",
+               .cmd    = DTV_FREQUENCY,
+               .set    = 1,
+       },
+       [DTV_BANDWIDTH] = {
+               .name   = "DTV_BANDWIDTH",
+               .cmd    = DTV_BANDWIDTH,
+               .set    = 1,
+       },
+       [DTV_MODULATION] = {
+               .name   = "DTV_MODULATION",
+               .cmd    = DTV_MODULATION,
+               .set    = 1,
+       },
+       [DTV_INVERSION] = {
+               .name   = "DTV_INVERSION",
+               .cmd    = DTV_INVERSION,
+               .set    = 1,
+       },
+       [DTV_DISEQC_MASTER] = {
+               .name   = "DTV_DISEQC_MASTER",
+               .cmd    = DTV_DISEQC_MASTER,
+               .set    = 1,
+               .buffer = 1,
+       },
+       [DTV_SYMBOL_RATE] = {
+               .name   = "DTV_SYMBOL_RATE",
+               .cmd    = DTV_SYMBOL_RATE,
+               .set    = 1,
+       },
+       [DTV_INNER_FEC] = {
+               .name   = "DTV_INNER_FEC",
+               .cmd    = DTV_INNER_FEC,
+               .set    = 1,
+       },
+       [DTV_VOLTAGE] = {
+               .name   = "DTV_VOLTAGE",
+               .cmd    = DTV_VOLTAGE,
+               .set    = 1,
+       },
+       [DTV_TONE] = {
+               .name   = "DTV_TONE",
+               .cmd    = DTV_TONE,
+               .set    = 1,
+       },
+       [DTV_PILOT] = {
+               .name   = "DTV_PILOT",
+               .cmd    = DTV_PILOT,
+               .set    = 1,
+       },
+       [DTV_ROLLOFF] = {
+               .name   = "DTV_ROLLOFF",
+               .cmd    = DTV_ROLLOFF,
+               .set    = 1,
+       },
+       [DTV_DELIVERY_SYSTEM] = {
+               .name   = "DTV_DELIVERY_SYSTEM",
+               .cmd    = DTV_DELIVERY_SYSTEM,
+               .set    = 1,
+       },
+       [DTV_ISDB_SEGMENT_IDX] = {
+               .name   = "DTV_ISDB_SEGMENT_IDX",
+               .cmd    = DTV_ISDB_SEGMENT_IDX,
+               .set    = 1,
+       },
+       [DTV_ISDB_SEGMENT_WIDTH] = {
+               .name   = "DTV_ISDB_SEGMENT_WIDTH",
+               .cmd    = DTV_ISDB_SEGMENT_WIDTH,
+               .set    = 1,
+       },
+
+       /* Get */
+       [DTV_DISEQC_SLAVE_REPLY] = {
+               .name   = "DTV_DISEQC_SLAVE_REPLY",
+               .cmd    = DTV_DISEQC_SLAVE_REPLY,
+               .set    = 0,
+               .buffer = 1,
+       },
+       [DTV_ISDB_LAYERA_FEC] = {
+               .name   = "DTV_ISDB_LAYERA_FEC",
+               .cmd    = DTV_ISDB_LAYERA_FEC,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERA_MODULATION] = {
+               .name   = "DTV_ISDB_LAYERA_MODULATION",
+               .cmd    = DTV_ISDB_LAYERA_MODULATION,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERA_SEGMENT_WIDTH] = {
+               .name   = "DTV_ISDB_LAYERA_SEGMENT_WIDTH",
+               .cmd    = DTV_ISDB_LAYERA_SEGMENT_WIDTH,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERB_FEC] = {
+               .name   = "DTV_ISDB_LAYERB_FEC",
+               .cmd    = DTV_ISDB_LAYERB_FEC,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERB_MODULATION] = {
+               .name   = "DTV_ISDB_LAYERB_MODULATION",
+               .cmd    = DTV_ISDB_LAYERB_MODULATION,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERB_SEGMENT_WIDTH] = {
+               .name   = "DTV_ISDB_LAYERB_SEGMENT_WIDTH",
+               .cmd    = DTV_ISDB_LAYERB_SEGMENT_WIDTH,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERC_FEC] = {
+               .name   = "DTV_ISDB_LAYERC_FEC",
+               .cmd    = DTV_ISDB_LAYERC_FEC,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERC_MODULATION] = {
+               .name   = "DTV_ISDB_LAYERC_MODULATION",
+               .cmd    = DTV_ISDB_LAYERC_MODULATION,
+               .set    = 0,
+       },
+       [DTV_ISDB_LAYERC_SEGMENT_WIDTH] = {
+               .name   = "DTV_ISDB_LAYERC_SEGMENT_WIDTH",
+               .cmd    = DTV_ISDB_LAYERC_SEGMENT_WIDTH,
+               .set    = 0,
+       },
+};
+
+void dtv_property_dump(struct dtv_property *tvp)
+{
+       int i;
+
+       printk("%s() tvp.cmd    = 0x%08x (%s)\n"
+               ,__FUNCTION__
+               ,tvp->cmd
+               ,dtv_cmds[ tvp->cmd ].name);
+
+       if(dtv_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;
+}
+
+/* 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.
+ */
+void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+       printk("%s()\n", __FUNCTION__);
+
+       c->frequency = p->frequency;
+       c->inversion = p->inversion;
+
+       switch (fe->ops.info.type) {
+       case FE_QPSK:
+               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:
+               c->bandwidth = p->u.ofdm.bandwidth;
+               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.
+ */
+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;
+
+       printk("%s()\n", __FUNCTION__);
+
+       p->frequency = c->frequency;
+       p->inversion = c->inversion;
+
+       switch (fe->ops.info.type) {
+       case FE_QPSK:
+               printk("%s() Preparing QPSK req\n", __FUNCTION__);
+               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:
+               printk("%s() Preparing QAM req\n", __FUNCTION__);
+               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:
+               printk("%s() Preparing OFDM req\n", __FUNCTION__);
+               p->u.ofdm.bandwidth = c->bandwidth;
+               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:
+               printk("%s() Preparing VSB req\n", __FUNCTION__);
+               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.
+ */
+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;
+
+       printk("%s()\n", __FUNCTION__);
+
+       p->frequency = c->frequency;
+       p->inversion = c->inversion;
+
+       switch(c->modulation) {
+       case _8PSK:
+       case _16APSK:
+       case NBC_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 = 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;
+       }
+}
+
+void dtv_property_cache_submit(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+       printk("%s()\n", __FUNCTION__);
+
+       /* 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)) {
+
+               printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation);
+               dtv_property_legacy_params_sync(fe);
+
+       } else {
+               printk("%s() adv, modulation = %d\n", __FUNCTION__, 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);
+
+int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp,
+       struct inode *inode, struct file *file)
+{
+       int r = 0;
+
+       printk("%s()\n", __FUNCTION__);
+
+       dtv_property_dump(tvp);
+
+       /* 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:
+               tvp->u.data = fe->dtv_property_cache.bandwidth;
+               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;
+
+       /* ISDB-T Support here */
+       case DTV_ISDB_SEGMENT_IDX:
+               tvp->u.data = fe->dtv_property_cache.isdb_segment_idx;
+               break;
+       case DTV_ISDB_SEGMENT_WIDTH:
+               tvp->u.data = fe->dtv_property_cache.isdb_segment_width;
+               break;
+       case DTV_ISDB_LAYERA_FEC:
+               tvp->u.data = fe->dtv_property_cache.isdb_layera_fec;
+               break;
+       case DTV_ISDB_LAYERA_MODULATION:
+               tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation;
+               break;
+       case DTV_ISDB_LAYERA_SEGMENT_WIDTH:
+               tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width;
+               break;
+       case DTV_ISDB_LAYERB_FEC:
+               tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec;
+               break;
+       case DTV_ISDB_LAYERB_MODULATION:
+               tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation;
+               break;
+       case DTV_ISDB_LAYERB_SEGMENT_WIDTH:
+               tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width;
+               break;
+       case DTV_ISDB_LAYERC_FEC:
+               tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec;
+               break;
+       case DTV_ISDB_LAYERC_MODULATION:
+               tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation;
+               break;
+       case DTV_ISDB_LAYERC_SEGMENT_WIDTH:
+               tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width;
+               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;
+       default:
+               r = -1;
+       }
+
+       return r;
+}
+
+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;
+       printk("%s()\n", __FUNCTION__);
+       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.
+                */
+               printk("%s() Flushing property cache\n", __FUNCTION__);
+               memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties));
+               fe->dtv_property_cache.state = tvp->cmd;
+               fe->dtv_property_cache.delivery_system = SYS_UNDEFINED;
+               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;
+               printk("%s() Finalised property cache\n", __FUNCTION__);
+               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:
+               fe->dtv_property_cache.bandwidth = 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;
+
+       /* ISDB-T Support here */
+       case DTV_ISDB_SEGMENT_IDX:
+               fe->dtv_property_cache.isdb_segment_idx = tvp->u.data;
+               break;
+       case DTV_ISDB_SEGMENT_WIDTH:
+               fe->dtv_property_cache.isdb_segment_width = 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;
+       default:
+               r = -1;
+       }
+
+       return r;
+}
+
 static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
                        unsigned int cmd, void *parg)
 {
@@ -724,9 +1284,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\n", __func__);
 
-       if (!fe || fepriv->exit)
+       if (fepriv->exit)
                return -ENODEV;
 
        if ((file->f_flags & O_ACCMODE) == O_RDONLY &&
@@ -737,10 +1297,115 @@ 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
+               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) {
+               printk("%s() FE_SET_PROPERTY\n", __FUNCTION__);
+
+               tvps = (struct dtv_properties __user *)parg;
+
+               printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+               printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+               /* Put an arbitrary limit on the number of messages that can
+                * be sent at once */
+               if (tvps->num > DTV_IOCTL_MAX_MSGS)
+                       return -EINVAL;
+
+               tvp = (struct dtv_property *) 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++)
+                       err |= dtv_property_process_set(fe, tvp + i, inode, file);
+
+               if(fe->dtv_property_cache.state == DTV_TUNE) {
+                       printk("%s() Property cache is full, tuning\n", __FUNCTION__);
+               }
+
+       } else
+       if(cmd == FE_GET_PROPERTY) {
+               printk("%s() FE_GET_PROPERTY\n", __FUNCTION__);
+
+               tvps = (struct dtv_properties __user *)parg;
+
+               printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+               printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+               /* Put an arbitrary limit on the number of messages that can
+                * be sent at once */
+               if (tvps->num > DTV_IOCTL_MAX_MSGS)
+                       return -EINVAL;
+
+               tvp = (struct dtv_property *) 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++)
+                       err |= dtv_property_process_get(fe, tvp + i, inode, file);
+
+               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 err = -EOPNOTSUPP;
+
        switch (cmd) {
        case FE_GET_INFO: {
                struct dvb_frontend_info* info = parg;
                memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info));
+               dvb_frontend_get_frequeny_limits(fe, &info->frequency_min, &info->frequency_max);
 
                /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't
                 * do it, it is done for it. */
@@ -849,13 +1514,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));
@@ -868,18 +1533,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]));
                        }
@@ -902,8 +1567,22 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
        case FE_SET_FRONTEND: {
                struct dvb_frontend_tune_settings fetunesettings;
 
-               memcpy (&fepriv->parameters, parg,
-                       sizeof (struct dvb_frontend_parameters));
+               dtv_property_cache_sync(fe, &fepriv->parameters);
+
+               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));
+               }
 
                memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
                memcpy(&fetunesettings.parameters, parg,
@@ -982,17 +1661,17 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
                break;
        };
 
-       up (&fepriv->sem);
        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);
 
@@ -1009,20 +1688,17 @@ static int dvb_frontend_open(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 ((ret = dvb_generic_open (inode, file)) < 0)
-               return ret;
-
-       if (fe->ops.ts_bus_ctrl) {
-               if ((ret = fe->ops.ts_bus_ctrl (fe, 1)) < 0) {
-                       dvb_generic_release (inode, file);
+       if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
+               if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
                        return ret;
-               }
        }
 
-       if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+       if ((ret = dvb_generic_open (inode, file)) < 0)
+               goto err1;
 
+       if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
                /* normal tune mode when opened R/W */
                fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT;
                fepriv->tone = -1;
@@ -1030,13 +1706,20 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
 
                ret = dvb_frontend_start (fe);
                if (ret)
-                       dvb_generic_release (inode, file);
+                       goto err2;
 
                /*  empty event queue */
                fepriv->events.eventr = fepriv->events.eventw = 0;
        }
 
        return ret;
+
+err2:
+       dvb_generic_release(inode, file);
+err1:
+       if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
+               fe->ops.ts_bus_ctrl(fe, 0);
+       return ret;
 }
 
 static int dvb_frontend_release(struct inode *inode, struct file *file)
@@ -1044,16 +1727,26 @@ static int dvb_frontend_release(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;
+       int ret;
 
-       dprintk ("%s\n", __FUNCTION__);
+       dprintk ("%s\n", __func__);
 
        if ((file->f_flags & O_ACCMODE) != O_RDONLY)
                fepriv->release_jiffies = jiffies;
 
-       if (fe->ops.ts_bus_ctrl)
-               fe->ops.ts_bus_ctrl (fe, 0);
+       ret = dvb_generic_release (inode, file);
 
-       return dvb_generic_release (inode, file);
+       if (dvbdev->users == -1) {
+               if (fepriv->exit == 1) {
+                       fops_put(file->f_op);
+                       file->f_op = NULL;
+                       wake_up(&dvbdev->wait_queue);
+               }
+               if (fe->ops.ts_bus_ctrl)
+                       fe->ops.ts_bus_ctrl(fe, 0);
+       }
+
+       return ret;
 }
 
 static struct file_operations dvb_frontend_fops = {
@@ -1076,7 +1769,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;
@@ -1091,7 +1784,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
        init_MUTEX (&fepriv->sem);
        init_waitqueue_head (&fepriv->wait_queue);
        init_waitqueue_head (&fepriv->events.wait_queue);
-       init_MUTEX (&fepriv->events.sem);
+       mutex_init(&fepriv->events.mtx);
        fe->dvb = dvb;
        fepriv->inversion = INVERSION_OFF;
 
@@ -1110,11 +1803,18 @@ 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_unregister_device (fepriv->dvbdev);
        dvb_frontend_stop (fe);
+       mutex_unlock(&frontend_mutex);
+
+       if (fepriv->dvbdev->users < -1)
+               wait_event(fepriv->dvbdev->wait_queue,
+                               fepriv->dvbdev->users==-1);
+
+       mutex_lock(&frontend_mutex);
+       dvb_unregister_device (fepriv->dvbdev);
 
        /* fe is invalid now */
        kfree(fepriv);
@@ -1123,7 +1823,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;
@@ -1136,6 +1836,10 @@ void dvb_frontend_detach(struct dvb_frontend* fe)
                fe->ops.tuner_ops.release(fe);
                symbol_put_addr(fe->ops.tuner_ops.release);
        }
+       if (fe->ops.analog_ops.release) {
+               fe->ops.analog_ops.release(fe);
+               symbol_put_addr(fe->ops.analog_ops.release);
+       }
        ptr = (void*)fe->ops.release;
        if (ptr) {
                fe->ops.release(fe);
@@ -1149,6 +1853,8 @@ void dvb_frontend_detach(struct dvb_frontend* fe)
                fe->ops.release_sec(fe);
        if (fe->ops.tuner_ops.release)
                fe->ops.tuner_ops.release(fe);
+       if (fe->ops.analog_ops.release)
+               fe->ops.analog_ops.release(fe);
        if (fe->ops.release)
                fe->ops.release(fe);
 }