Commit 18192a77 authored by Jaedon Shin's avatar Jaedon Shin Committed by Mauro Carvalho Chehab
Browse files

media: dvb_frontend: Add commands implementation for compat ioct



The dtv_properties structure and the dtv_property structure are
different sizes in 32-bit and 64-bit system. This patch provides
FE_SET_PROPERTY and FE_GET_PROPERTY ioctl commands implementation for
32-bit user space applications.

Signed-off-by: default avatarJaedon Shin <jaedon.shin@gmail.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent c2dfd227
Loading
Loading
Loading
Loading
+131 −0
Original line number Diff line number Diff line
@@ -1979,9 +1979,140 @@ static long dvb_frontend_ioctl(struct file *file, unsigned int cmd,
}

#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