Merge tag 'media/v4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-2.6-block.git] / drivers / media / dvb-core / dvb_frontend.c
index 48e16fd003a7d9fb9503b4db57afd5e7bcb7fac1..87fc1bcae5ae6c5d610d02321de48e5895724932 100644 (file)
 #include <linux/jiffies.h>
 #include <linux/kthread.h>
 #include <linux/ktime.h>
+#include <linux/compat.h>
 #include <asm/processor.h>
 
-#include "dvb_frontend.h"
-#include "dvbdev.h"
+#include <media/dvb_frontend.h>
+#include <media/dvbdev.h>
 #include <linux/dvb/version.h>
 
 static int dvb_frontend_debug;
@@ -150,8 +151,7 @@ static void __dvb_frontend_free(struct dvb_frontend *fe)
 
        dvb_frontend_invoke_release(fe, fe->ops.release);
 
-       if (fepriv)
-               kfree(fepriv);
+       kfree(fepriv);
 }
 
 static void dvb_frontend_free(struct kref *ref)
@@ -982,6 +982,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
        }
 
        c->stream_id = NO_STREAM_ID_FILTER;
+       c->scrambling_sequence_index = 0;/* default sequence */
 
        switch (c->delivery_system) {
        case SYS_DVBS:
@@ -1072,6 +1073,7 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
 
        _DTV_CMD(DTV_STREAM_ID, 1, 0),
        _DTV_CMD(DTV_DVBT2_PLP_ID_LEGACY, 1, 0),
+       _DTV_CMD(DTV_SCRAMBLING_SEQUENCE_INDEX, 1, 0),
        _DTV_CMD(DTV_LNA, 1, 0),
 
        /* Get */
@@ -1417,6 +1419,11 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
                tvp->u.data = c->stream_id;
                break;
 
+       /* Physical layer scrambling support */
+       case DTV_SCRAMBLING_SEQUENCE_INDEX:
+               tvp->u.data = c->scrambling_sequence_index;
+               break;
+
        /* ATSC-MH */
        case DTV_ATSCMH_FIC_VER:
                tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver;
@@ -1900,6 +1907,11 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
                c->stream_id = data;
                break;
 
+       /* Physical layer scrambling support */
+       case DTV_SCRAMBLING_SEQUENCE_INDEX:
+               c->scrambling_sequence_index = data;
+               break;
+
        /* ATSC-MH */
        case DTV_ATSCMH_PARADE_ID:
                fe->dtv_property_cache.atscmh_parade_id = data;
@@ -1923,7 +1935,8 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
        return r;
 }
 
-static int dvb_frontend_ioctl(struct file *file, unsigned int cmd, void *parg)
+static int dvb_frontend_do_ioctl(struct file *file, unsigned int cmd,
+                                void *parg)
 {
        struct dvb_device *dvbdev = file->private_data;
        struct dvb_frontend *fe = dvbdev->priv;
@@ -1966,6 +1979,156 @@ static int dvb_frontend_ioctl(struct file *file, unsigned int cmd, void *parg)
        return err;
 }
 
+static long dvb_frontend_ioctl(struct file *file, unsigned int cmd,
+                              unsigned long arg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+
+       if (!dvbdev)
+               return -ENODEV;
+
+       return dvb_usercopy(file, cmd, arg, dvb_frontend_do_ioctl);
+}
+
+#ifdef CONFIG_COMPAT
+struct compat_dtv_property {
+       __u32 cmd;
+       __u32 reserved[3];
+       union {
+               __u32 data;
+               struct dtv_fe_stats st;
+               struct {
+                       __u8 data[32];
+                       __u32 len;
+                       __u32 reserved1[3];
+                       compat_uptr_t reserved2;
+               } buffer;
+       } u;
+       int result;
+} __attribute__ ((packed));
+
+struct compat_dtv_properties {
+       __u32 num;
+       compat_uptr_t props;
+};
+
+#define COMPAT_FE_SET_PROPERTY    _IOW('o', 82, struct compat_dtv_properties)
+#define COMPAT_FE_GET_PROPERTY    _IOR('o', 83, struct compat_dtv_properties)
+
+static int dvb_frontend_handle_compat_ioctl(struct file *file, unsigned int cmd,
+                                           unsigned long arg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_frontend *fe = dvbdev->priv;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       int i, err = 0;
+
+       if (cmd == COMPAT_FE_SET_PROPERTY) {
+               struct compat_dtv_properties prop, *tvps = NULL;
+               struct compat_dtv_property *tvp = NULL;
+
+               if (copy_from_user(&prop, compat_ptr(arg), sizeof(prop)))
+                       return -EFAULT;
+
+               tvps = &prop;
+
+               /*
+                * Put an arbitrary limit on the number of messages that can
+                * be sent at once
+                */
+               if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
+                       return -EINVAL;
+
+               tvp = memdup_user(compat_ptr(tvps->props), tvps->num * sizeof(*tvp));
+               if (IS_ERR(tvp))
+                       return PTR_ERR(tvp);
+
+               for (i = 0; i < tvps->num; i++) {
+                       err = dtv_property_process_set(fe, file,
+                                                       (tvp + i)->cmd,
+                                                       (tvp + i)->u.data);
+                       if (err < 0) {
+                               kfree(tvp);
+                               return err;
+                       }
+               }
+               kfree(tvp);
+       } else if (cmd == COMPAT_FE_GET_PROPERTY) {
+               struct compat_dtv_properties prop, *tvps = NULL;
+               struct compat_dtv_property *tvp = NULL;
+               struct dtv_frontend_properties getp = fe->dtv_property_cache;
+
+               if (copy_from_user(&prop, compat_ptr(arg), sizeof(prop)))
+                       return -EFAULT;
+
+               tvps = &prop;
+
+               /*
+                * Put an arbitrary limit on the number of messages that can
+                * be sent at once
+                */
+               if (!tvps->num || (tvps->num > DTV_IOCTL_MAX_MSGS))
+                       return -EINVAL;
+
+               tvp = memdup_user(compat_ptr(tvps->props), tvps->num * sizeof(*tvp));
+               if (IS_ERR(tvp))
+                       return PTR_ERR(tvp);
+
+               /*
+                * Let's use our own copy of property cache, in order to
+                * avoid mangling with DTV zigzag logic, as drivers might
+                * return crap, if they don't check if the data is available
+                * before updating the properties cache.
+                */
+               if (fepriv->state != FESTATE_IDLE) {
+                       err = dtv_get_frontend(fe, &getp, NULL);
+                       if (err < 0) {
+                               kfree(tvp);
+                               return err;
+                       }
+               }
+               for (i = 0; i < tvps->num; i++) {
+                       err = dtv_property_process_get(
+                           fe, &getp, (struct dtv_property *)tvp + i, file);
+                       if (err < 0) {
+                               kfree(tvp);
+                               return err;
+                       }
+               }
+
+               if (copy_to_user((void __user *)compat_ptr(tvps->props), tvp,
+                                tvps->num * sizeof(struct compat_dtv_property))) {
+                       kfree(tvp);
+                       return -EFAULT;
+               }
+               kfree(tvp);
+       }
+
+       return err;
+}
+
+static long dvb_frontend_compat_ioctl(struct file *file, unsigned int cmd,
+                                     unsigned long arg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_frontend *fe = dvbdev->priv;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
+       int err;
+
+       if (cmd == COMPAT_FE_SET_PROPERTY || cmd == COMPAT_FE_GET_PROPERTY) {
+               if (down_interruptible(&fepriv->sem))
+                       return -ERESTARTSYS;
+
+               err = dvb_frontend_handle_compat_ioctl(file, cmd, arg);
+
+               up(&fepriv->sem);
+               return err;
+       }
+
+       return dvb_frontend_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
 static int dtv_set_frontend(struct dvb_frontend *fe)
 {
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
@@ -2110,7 +2273,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
        struct dvb_frontend *fe = dvbdev->priv;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-       int i, err;
+       int i, err = -ENOTSUPP;
 
        dev_dbg(fe->dvb->device, "%s:\n", __func__);
 
@@ -2145,6 +2308,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
                        }
                }
                kfree(tvp);
+               err = 0;
                break;
        }
        case FE_GET_PROPERTY: {
@@ -2196,6 +2360,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
                        return -EFAULT;
                }
                kfree(tvp);
+               err = 0;
                break;
        }
 
@@ -2647,7 +2812,10 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
 
 static const struct file_operations dvb_frontend_fops = {
        .owner          = THIS_MODULE,
-       .unlocked_ioctl = dvb_generic_ioctl,
+       .unlocked_ioctl = dvb_frontend_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = dvb_frontend_compat_ioctl,
+#endif
        .poll           = dvb_frontend_poll,
        .open           = dvb_frontend_open,
        .release        = dvb_frontend_release,
@@ -2715,7 +2883,6 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
 #if defined(CONFIG_MEDIA_CONTROLLER_DVB)
                .name = fe->ops.info.name,
 #endif
-               .kernel_ioctl = dvb_frontend_ioctl
        };
 
        dev_dbg(dvb->device, "%s:\n", __func__);