Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 229b53c9 authored by Al Viro's avatar Al Viro
Browse files

take floppy compat ioctls to sodding floppy.c



all other drivers recognizing those ioctls are very much *not*
biarch.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent e5f699d4
Loading
Loading
Loading
Loading
+0 −340
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@
#include <linux/cdrom.h>
#include <linux/compat.h>
#include <linux/elevator.h>
#include <linux/fd.h>
#include <linux/hdreg.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
@@ -209,318 +208,6 @@ static int compat_blkpg_ioctl(struct block_device *bdev, fmode_t mode,
#define BLKBSZSET_32		_IOW(0x12, 113, int)
#define BLKGETSIZE64_32		_IOR(0x12, 114, int)

struct compat_floppy_drive_params {
	char		cmos;
	compat_ulong_t	max_dtr;
	compat_ulong_t	hlt;
	compat_ulong_t	hut;
	compat_ulong_t	srt;
	compat_ulong_t	spinup;
	compat_ulong_t	spindown;
	unsigned char	spindown_offset;
	unsigned char	select_delay;
	unsigned char	rps;
	unsigned char	tracks;
	compat_ulong_t	timeout;
	unsigned char	interleave_sect;
	struct floppy_max_errors max_errors;
	char		flags;
	char		read_track;
	short		autodetect[8];
	compat_int_t	checkfreq;
	compat_int_t	native_format;
};

struct compat_floppy_drive_struct {
	signed char	flags;
	compat_ulong_t	spinup_date;
	compat_ulong_t	select_date;
	compat_ulong_t	first_read_date;
	short		probed_format;
	short		track;
	short		maxblock;
	short		maxtrack;
	compat_int_t	generation;
	compat_int_t	keep_data;
	compat_int_t	fd_ref;
	compat_int_t	fd_device;
	compat_int_t	last_checked;
	compat_caddr_t dmabuf;
	compat_int_t	bufblocks;
};

struct compat_floppy_fdc_state {
	compat_int_t	spec1;
	compat_int_t	spec2;
	compat_int_t	dtr;
	unsigned char	version;
	unsigned char	dor;
	compat_ulong_t	address;
	unsigned int	rawcmd:2;
	unsigned int	reset:1;
	unsigned int	need_configure:1;
	unsigned int	perp_mode:2;
	unsigned int	has_fifo:1;
	unsigned int	driver_version;
	unsigned char	track[4];
};

struct compat_floppy_write_errors {
	unsigned int	write_errors;
	compat_ulong_t	first_error_sector;
	compat_int_t	first_error_generation;
	compat_ulong_t	last_error_sector;
	compat_int_t	last_error_generation;
	compat_uint_t	badness;
};

#define FDSETPRM32 _IOW(2, 0x42, struct compat_floppy_struct)
#define FDDEFPRM32 _IOW(2, 0x43, struct compat_floppy_struct)
#define FDSETDRVPRM32 _IOW(2, 0x90, struct compat_floppy_drive_params)
#define FDGETDRVPRM32 _IOR(2, 0x11, struct compat_floppy_drive_params)
#define FDGETDRVSTAT32 _IOR(2, 0x12, struct compat_floppy_drive_struct)
#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct compat_floppy_drive_struct)
#define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state)
#define FDWERRORGET32  _IOR(2, 0x17, struct compat_floppy_write_errors)

static struct {
	unsigned int	cmd32;
	unsigned int	cmd;
} fd_ioctl_trans_table[] = {
	{ FDSETPRM32, FDSETPRM },
	{ FDDEFPRM32, FDDEFPRM },
	{ FDGETPRM32, FDGETPRM },
	{ FDSETDRVPRM32, FDSETDRVPRM },
	{ FDGETDRVPRM32, FDGETDRVPRM },
	{ FDGETDRVSTAT32, FDGETDRVSTAT },
	{ FDPOLLDRVSTAT32, FDPOLLDRVSTAT },
	{ FDGETFDCSTAT32, FDGETFDCSTAT },
	{ FDWERRORGET32, FDWERRORGET }
};

#define NR_FD_IOCTL_TRANS ARRAY_SIZE(fd_ioctl_trans_table)

static int compat_fd_ioctl(struct block_device *bdev, fmode_t mode,
		unsigned int cmd, unsigned long arg)
{
	mm_segment_t old_fs = get_fs();
	void *karg = NULL;
	unsigned int kcmd = 0;
	int i, err;

	for (i = 0; i < NR_FD_IOCTL_TRANS; i++)
		if (cmd == fd_ioctl_trans_table[i].cmd32) {
			kcmd = fd_ioctl_trans_table[i].cmd;
			break;
		}
	if (!kcmd)
		return -EINVAL;

	switch (cmd) {
	case FDSETPRM32:
	case FDDEFPRM32:
	case FDGETPRM32:
	{
		compat_uptr_t name;
		struct compat_floppy_struct __user *uf;
		struct floppy_struct *f;

		uf = compat_ptr(arg);
		f = karg = kmalloc(sizeof(struct floppy_struct), GFP_KERNEL);
		if (!karg)
			return -ENOMEM;
		if (cmd == FDGETPRM32)
			break;
		err = __get_user(f->size, &uf->size);
		err |= __get_user(f->sect, &uf->sect);
		err |= __get_user(f->head, &uf->head);
		err |= __get_user(f->track, &uf->track);
		err |= __get_user(f->stretch, &uf->stretch);
		err |= __get_user(f->gap, &uf->gap);
		err |= __get_user(f->rate, &uf->rate);
		err |= __get_user(f->spec1, &uf->spec1);
		err |= __get_user(f->fmt_gap, &uf->fmt_gap);
		err |= __get_user(name, &uf->name);
		f->name = compat_ptr(name);
		if (err) {
			err = -EFAULT;
			goto out;
		}
		break;
	}
	case FDSETDRVPRM32:
	case FDGETDRVPRM32:
	{
		struct compat_floppy_drive_params __user *uf;
		struct floppy_drive_params *f;

		uf = compat_ptr(arg);
		f = karg = kmalloc(sizeof(struct floppy_drive_params), GFP_KERNEL);
		if (!karg)
			return -ENOMEM;
		if (cmd == FDGETDRVPRM32)
			break;
		err = __get_user(f->cmos, &uf->cmos);
		err |= __get_user(f->max_dtr, &uf->max_dtr);
		err |= __get_user(f->hlt, &uf->hlt);
		err |= __get_user(f->hut, &uf->hut);
		err |= __get_user(f->srt, &uf->srt);
		err |= __get_user(f->spinup, &uf->spinup);
		err |= __get_user(f->spindown, &uf->spindown);
		err |= __get_user(f->spindown_offset, &uf->spindown_offset);
		err |= __get_user(f->select_delay, &uf->select_delay);
		err |= __get_user(f->rps, &uf->rps);
		err |= __get_user(f->tracks, &uf->tracks);
		err |= __get_user(f->timeout, &uf->timeout);
		err |= __get_user(f->interleave_sect, &uf->interleave_sect);
		err |= __copy_from_user(&f->max_errors, &uf->max_errors, sizeof(f->max_errors));
		err |= __get_user(f->flags, &uf->flags);
		err |= __get_user(f->read_track, &uf->read_track);
		err |= __copy_from_user(f->autodetect, uf->autodetect, sizeof(f->autodetect));
		err |= __get_user(f->checkfreq, &uf->checkfreq);
		err |= __get_user(f->native_format, &uf->native_format);
		if (err) {
			err = -EFAULT;
			goto out;
		}
		break;
	}
	case FDGETDRVSTAT32:
	case FDPOLLDRVSTAT32:
		karg = kmalloc(sizeof(struct floppy_drive_struct), GFP_KERNEL);
		if (!karg)
			return -ENOMEM;
		break;
	case FDGETFDCSTAT32:
		karg = kmalloc(sizeof(struct floppy_fdc_state), GFP_KERNEL);
		if (!karg)
			return -ENOMEM;
		break;
	case FDWERRORGET32:
		karg = kmalloc(sizeof(struct floppy_write_errors), GFP_KERNEL);
		if (!karg)
			return -ENOMEM;
		break;
	default:
		return -EINVAL;
	}
	set_fs(KERNEL_DS);
	err = __blkdev_driver_ioctl(bdev, mode, kcmd, (unsigned long)karg);
	set_fs(old_fs);
	if (err)
		goto out;
	switch (cmd) {
	case FDGETPRM32:
	{
		struct floppy_struct *f = karg;
		struct compat_floppy_struct __user *uf = compat_ptr(arg);

		err = __put_user(f->size, &uf->size);
		err |= __put_user(f->sect, &uf->sect);
		err |= __put_user(f->head, &uf->head);
		err |= __put_user(f->track, &uf->track);
		err |= __put_user(f->stretch, &uf->stretch);
		err |= __put_user(f->gap, &uf->gap);
		err |= __put_user(f->rate, &uf->rate);
		err |= __put_user(f->spec1, &uf->spec1);
		err |= __put_user(f->fmt_gap, &uf->fmt_gap);
		err |= __put_user((u64)f->name, (compat_caddr_t __user *)&uf->name);
		break;
	}
	case FDGETDRVPRM32:
	{
		struct compat_floppy_drive_params __user *uf;
		struct floppy_drive_params *f = karg;

		uf = compat_ptr(arg);
		err = __put_user(f->cmos, &uf->cmos);
		err |= __put_user(f->max_dtr, &uf->max_dtr);
		err |= __put_user(f->hlt, &uf->hlt);
		err |= __put_user(f->hut, &uf->hut);
		err |= __put_user(f->srt, &uf->srt);
		err |= __put_user(f->spinup, &uf->spinup);
		err |= __put_user(f->spindown, &uf->spindown);
		err |= __put_user(f->spindown_offset, &uf->spindown_offset);
		err |= __put_user(f->select_delay, &uf->select_delay);
		err |= __put_user(f->rps, &uf->rps);
		err |= __put_user(f->tracks, &uf->tracks);
		err |= __put_user(f->timeout, &uf->timeout);
		err |= __put_user(f->interleave_sect, &uf->interleave_sect);
		err |= __copy_to_user(&uf->max_errors, &f->max_errors, sizeof(f->max_errors));
		err |= __put_user(f->flags, &uf->flags);
		err |= __put_user(f->read_track, &uf->read_track);
		err |= __copy_to_user(uf->autodetect, f->autodetect, sizeof(f->autodetect));
		err |= __put_user(f->checkfreq, &uf->checkfreq);
		err |= __put_user(f->native_format, &uf->native_format);
		break;
	}
	case FDGETDRVSTAT32:
	case FDPOLLDRVSTAT32:
	{
		struct compat_floppy_drive_struct __user *uf;
		struct floppy_drive_struct *f = karg;

		uf = compat_ptr(arg);
		err = __put_user(f->flags, &uf->flags);
		err |= __put_user(f->spinup_date, &uf->spinup_date);
		err |= __put_user(f->select_date, &uf->select_date);
		err |= __put_user(f->first_read_date, &uf->first_read_date);
		err |= __put_user(f->probed_format, &uf->probed_format);
		err |= __put_user(f->track, &uf->track);
		err |= __put_user(f->maxblock, &uf->maxblock);
		err |= __put_user(f->maxtrack, &uf->maxtrack);
		err |= __put_user(f->generation, &uf->generation);
		err |= __put_user(f->keep_data, &uf->keep_data);
		err |= __put_user(f->fd_ref, &uf->fd_ref);
		err |= __put_user(f->fd_device, &uf->fd_device);
		err |= __put_user(f->last_checked, &uf->last_checked);
		err |= __put_user((u64)f->dmabuf, &uf->dmabuf);
		err |= __put_user((u64)f->bufblocks, &uf->bufblocks);
		break;
	}
	case FDGETFDCSTAT32:
	{
		struct compat_floppy_fdc_state __user *uf;
		struct floppy_fdc_state *f = karg;

		uf = compat_ptr(arg);
		err = __put_user(f->spec1, &uf->spec1);
		err |= __put_user(f->spec2, &uf->spec2);
		err |= __put_user(f->dtr, &uf->dtr);
		err |= __put_user(f->version, &uf->version);
		err |= __put_user(f->dor, &uf->dor);
		err |= __put_user(f->address, &uf->address);
		err |= __copy_to_user((char __user *)&uf->address + sizeof(uf->address),
				   (char *)&f->address + sizeof(f->address), sizeof(int));
		err |= __put_user(f->driver_version, &uf->driver_version);
		err |= __copy_to_user(uf->track, f->track, sizeof(f->track));
		break;
	}
	case FDWERRORGET32:
	{
		struct compat_floppy_write_errors __user *uf;
		struct floppy_write_errors *f = karg;

		uf = compat_ptr(arg);
		err = __put_user(f->write_errors, &uf->write_errors);
		err |= __put_user(f->first_error_sector, &uf->first_error_sector);
		err |= __put_user(f->first_error_generation, &uf->first_error_generation);
		err |= __put_user(f->last_error_sector, &uf->last_error_sector);
		err |= __put_user(f->last_error_generation, &uf->last_error_generation);
		err |= __put_user(f->badness, &uf->badness);
		break;
	}
	default:
		break;
	}
	if (err)
		err = -EFAULT;

out:
	kfree(karg);
	return err;
}

static int compat_blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
			unsigned cmd, unsigned long arg)
{
@@ -537,16 +224,6 @@ static int compat_blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
	case HDIO_GET_ADDRESS:
	case HDIO_GET_BUSSTATE:
		return compat_hdio_ioctl(bdev, mode, cmd, arg);
	case FDSETPRM32:
	case FDDEFPRM32:
	case FDGETPRM32:
	case FDSETDRVPRM32:
	case FDGETDRVPRM32:
	case FDGETDRVSTAT32:
	case FDPOLLDRVSTAT32:
	case FDGETFDCSTAT32:
	case FDWERRORGET32:
		return compat_fd_ioctl(bdev, mode, cmd, arg);
	case CDROMREADAUDIO:
		return compat_cdrom_read_audio(bdev, mode, cmd, arg);
	case CDROM_SEND_PACKET:
@@ -566,23 +243,6 @@ static int compat_blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
	case HDIO_DRIVE_CMD:
	/* 0x330 is reserved -- it used to be HDIO_GETGEO_BIG */
	case 0x330:
	/* 0x02 -- Floppy ioctls */
	case FDMSGON:
	case FDMSGOFF:
	case FDSETEMSGTRESH:
	case FDFLUSH:
	case FDWERRORCLR:
	case FDSETMAXERRS:
	case FDGETMAXERRS:
	case FDGETDRVTYP:
	case FDEJECT:
	case FDCLRPRM:
	case FDFMTBEG:
	case FDFMTEND:
	case FDRESET:
	case FDTWADDLE:
	case FDFMTTRK:
	case FDRAWCMD:
	/* CDROM stuff */
	case CDROMPAUSE:
	case CDROMRESUME:
+328 −0
Original line number Diff line number Diff line
@@ -192,6 +192,7 @@ static int print_unex = 1;
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/async.h>
#include <linux/compat.h>

/*
 * PS/2 floppies have much slower step rates than regular floppies.
@@ -3568,6 +3569,330 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode,
	return ret;
}

#ifdef CONFIG_COMPAT

struct compat_floppy_drive_params {
	char		cmos;
	compat_ulong_t	max_dtr;
	compat_ulong_t	hlt;
	compat_ulong_t	hut;
	compat_ulong_t	srt;
	compat_ulong_t	spinup;
	compat_ulong_t	spindown;
	unsigned char	spindown_offset;
	unsigned char	select_delay;
	unsigned char	rps;
	unsigned char	tracks;
	compat_ulong_t	timeout;
	unsigned char	interleave_sect;
	struct floppy_max_errors max_errors;
	char		flags;
	char		read_track;
	short		autodetect[8];
	compat_int_t	checkfreq;
	compat_int_t	native_format;
};

struct compat_floppy_drive_struct {
	signed char	flags;
	compat_ulong_t	spinup_date;
	compat_ulong_t	select_date;
	compat_ulong_t	first_read_date;
	short		probed_format;
	short		track;
	short		maxblock;
	short		maxtrack;
	compat_int_t	generation;
	compat_int_t	keep_data;
	compat_int_t	fd_ref;
	compat_int_t	fd_device;
	compat_int_t	last_checked;
	compat_caddr_t dmabuf;
	compat_int_t	bufblocks;
};

struct compat_floppy_fdc_state {
	compat_int_t	spec1;
	compat_int_t	spec2;
	compat_int_t	dtr;
	unsigned char	version;
	unsigned char	dor;
	compat_ulong_t	address;
	unsigned int	rawcmd:2;
	unsigned int	reset:1;
	unsigned int	need_configure:1;
	unsigned int	perp_mode:2;
	unsigned int	has_fifo:1;
	unsigned int	driver_version;
	unsigned char	track[4];
};

struct compat_floppy_write_errors {
	unsigned int	write_errors;
	compat_ulong_t	first_error_sector;
	compat_int_t	first_error_generation;
	compat_ulong_t	last_error_sector;
	compat_int_t	last_error_generation;
	compat_uint_t	badness;
};

#define FDSETPRM32 _IOW(2, 0x42, struct compat_floppy_struct)
#define FDDEFPRM32 _IOW(2, 0x43, struct compat_floppy_struct)
#define FDSETDRVPRM32 _IOW(2, 0x90, struct compat_floppy_drive_params)
#define FDGETDRVPRM32 _IOR(2, 0x11, struct compat_floppy_drive_params)
#define FDGETDRVSTAT32 _IOR(2, 0x12, struct compat_floppy_drive_struct)
#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct compat_floppy_drive_struct)
#define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state)
#define FDWERRORGET32  _IOR(2, 0x17, struct compat_floppy_write_errors)

static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned int cmd,
		    struct compat_floppy_struct __user *arg)
{
	struct floppy_struct v;
	int drive, type;
	int err;

	BUILD_BUG_ON(offsetof(struct floppy_struct, name) !=
		     offsetof(struct compat_floppy_struct, name));

	if (!(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL)))
		return -EPERM;

	memset(&v, 0, sizeof(struct floppy_struct));
	if (copy_from_user(&v, arg, offsetof(struct floppy_struct, name)))
		return -EFAULT;

	mutex_lock(&floppy_mutex);
	drive = (long)bdev->bd_disk->private_data;
	type = ITYPE(UDRS->fd_device);
	err = set_geometry(cmd == FDSETPRM32 ? FDSETPRM : FDDEFPRM,
			&v, drive, type, bdev);
	mutex_unlock(&floppy_mutex);
	return err;
}

static int compat_get_prm(int drive,
			  struct compat_floppy_struct __user *arg)
{
	struct compat_floppy_struct v;
	struct floppy_struct *p;
	int err;

	memset(&v, 0, sizeof(v));
	mutex_lock(&floppy_mutex);
	err = get_floppy_geometry(drive, ITYPE(UDRS->fd_device), &p);
	if (err) {
		mutex_unlock(&floppy_mutex);
		return err;
	}
	memcpy(&v, p, offsetof(struct floppy_struct, name));
	mutex_unlock(&floppy_mutex);
	if (copy_to_user(arg, &v, sizeof(struct compat_floppy_struct)))
		return -EFAULT;
	return 0;
}

static int compat_setdrvprm(int drive,
			    struct compat_floppy_drive_params __user *arg)
{
	struct compat_floppy_drive_params v;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (copy_from_user(&v, arg, sizeof(struct compat_floppy_drive_params)))
		return -EFAULT;
	mutex_lock(&floppy_mutex);
	UDP->cmos = v.cmos;
	UDP->max_dtr = v.max_dtr;
	UDP->hlt = v.hlt;
	UDP->hut = v.hut;
	UDP->srt = v.srt;
	UDP->spinup = v.spinup;
	UDP->spindown = v.spindown;
	UDP->spindown_offset = v.spindown_offset;
	UDP->select_delay = v.select_delay;
	UDP->rps = v.rps;
	UDP->tracks = v.tracks;
	UDP->timeout = v.timeout;
	UDP->interleave_sect = v.interleave_sect;
	UDP->max_errors = v.max_errors;
	UDP->flags = v.flags;
	UDP->read_track = v.read_track;
	memcpy(UDP->autodetect, v.autodetect, sizeof(v.autodetect));
	UDP->checkfreq = v.checkfreq;
	UDP->native_format = v.native_format;
	mutex_unlock(&floppy_mutex);
	return 0;
}

static int compat_getdrvprm(int drive,
			    struct compat_floppy_drive_params __user *arg)
{
	struct compat_floppy_drive_params v;

	memset(&v, 0, sizeof(struct compat_floppy_drive_params));
	mutex_lock(&floppy_mutex);
	v.cmos = UDP->cmos;
	v.max_dtr = UDP->max_dtr;
	v.hlt = UDP->hlt;
	v.hut = UDP->hut;
	v.srt = UDP->srt;
	v.spinup = UDP->spinup;
	v.spindown = UDP->spindown;
	v.spindown_offset = UDP->spindown_offset;
	v.select_delay = UDP->select_delay;
	v.rps = UDP->rps;
	v.tracks = UDP->tracks;
	v.timeout = UDP->timeout;
	v.interleave_sect = UDP->interleave_sect;
	v.max_errors = UDP->max_errors;
	v.flags = UDP->flags;
	v.read_track = UDP->read_track;
	memcpy(v.autodetect, UDP->autodetect, sizeof(v.autodetect));
	v.checkfreq = UDP->checkfreq;
	v.native_format = UDP->native_format;
	mutex_unlock(&floppy_mutex);

	if (copy_from_user(arg, &v, sizeof(struct compat_floppy_drive_params)))
		return -EFAULT;
	return 0;
}

static int compat_getdrvstat(int drive, bool poll,
			    struct compat_floppy_drive_struct __user *arg)
{
	struct compat_floppy_drive_struct v;

	memset(&v, 0, sizeof(struct compat_floppy_drive_struct));
	mutex_lock(&floppy_mutex);

	if (poll) {
		if (lock_fdc(drive))
			goto Eintr;
		if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
			goto Eintr;
		process_fd_request();
	}
	v.spinup_date = UDRS->spinup_date;
	v.select_date = UDRS->select_date;
	v.first_read_date = UDRS->first_read_date;
	v.probed_format = UDRS->probed_format;
	v.track = UDRS->track;
	v.maxblock = UDRS->maxblock;
	v.maxtrack = UDRS->maxtrack;
	v.generation = UDRS->generation;
	v.keep_data = UDRS->keep_data;
	v.fd_ref = UDRS->fd_ref;
	v.fd_device = UDRS->fd_device;
	v.last_checked = UDRS->last_checked;
	v.dmabuf = (uintptr_t)UDRS->dmabuf;
	v.bufblocks = UDRS->bufblocks;
	mutex_unlock(&floppy_mutex);

	if (copy_from_user(arg, &v, sizeof(struct compat_floppy_drive_struct)))
		return -EFAULT;
	return 0;
Eintr:
	mutex_unlock(&floppy_mutex);
	return -EINTR;
}

static int compat_getfdcstat(int drive,
			    struct compat_floppy_fdc_state __user *arg)
{
	struct compat_floppy_fdc_state v32;
	struct floppy_fdc_state v;

	mutex_lock(&floppy_mutex);
	v = *UFDCS;
	mutex_unlock(&floppy_mutex);

	memset(&v32, 0, sizeof(struct compat_floppy_fdc_state));
	v32.spec1 = v.spec1;
	v32.spec2 = v.spec2;
	v32.dtr = v.dtr;
	v32.version = v.version;
	v32.dor = v.dor;
	v32.address = v.address;
	v32.rawcmd = v.rawcmd;
	v32.reset = v.reset;
	v32.need_configure = v.need_configure;
	v32.perp_mode = v.perp_mode;
	v32.has_fifo = v.has_fifo;
	v32.driver_version = v.driver_version;
	memcpy(v32.track, v.track, 4);
	if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_fdc_state)))
		return -EFAULT;
	return 0;
}

static int compat_werrorget(int drive,
			    struct compat_floppy_write_errors __user *arg)
{
	struct compat_floppy_write_errors v32;
	struct floppy_write_errors v;

	memset(&v32, 0, sizeof(struct compat_floppy_write_errors));
	mutex_lock(&floppy_mutex);
	v = *UDRWE;
	mutex_unlock(&floppy_mutex);
	v32.write_errors = v.write_errors;
	v32.first_error_sector = v.first_error_sector;
	v32.first_error_generation = v.first_error_generation;
	v32.last_error_sector = v.last_error_sector;
	v32.last_error_generation = v.last_error_generation;
	v32.badness = v.badness;
	if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_write_errors)))
		return -EFAULT;
	return 0;
}

static int fd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
		    unsigned long param)
{
	int drive = (long)bdev->bd_disk->private_data;
	switch (cmd) {
	case FDMSGON:
	case FDMSGOFF:
	case FDSETEMSGTRESH:
	case FDFLUSH:
	case FDWERRORCLR:
	case FDEJECT:
	case FDCLRPRM:
	case FDFMTBEG:
	case FDRESET:
	case FDTWADDLE:
		return fd_ioctl(bdev, mode, cmd, param);
	case FDSETMAXERRS:
	case FDGETMAXERRS:
	case FDGETDRVTYP:
	case FDFMTEND:
	case FDFMTTRK:
	case FDRAWCMD:
		return fd_ioctl(bdev, mode, cmd,
				(unsigned long)compat_ptr(param));
	case FDSETPRM32:
	case FDDEFPRM32:
		return compat_set_geometry(bdev, mode, cmd, compat_ptr(param));
	case FDGETPRM32:
		return compat_get_prm(drive, compat_ptr(param));
	case FDSETDRVPRM32:
		return compat_setdrvprm(drive, compat_ptr(param));
	case FDGETDRVPRM32:
		return compat_getdrvprm(drive, compat_ptr(param));
	case FDPOLLDRVSTAT32:
		return compat_getdrvstat(drive, true, compat_ptr(param));
	case FDGETDRVSTAT32:
		return compat_getdrvstat(drive, false, compat_ptr(param));
	case FDGETFDCSTAT32:
		return compat_getfdcstat(drive, compat_ptr(param));
	case FDWERRORGET32:
		return compat_werrorget(drive, compat_ptr(param));
	}
	return -EINVAL;
}
#endif

static void __init config_types(void)
{
	bool has_drive = false;
@@ -3885,6 +4210,9 @@ static const struct block_device_operations floppy_fops = {
	.getgeo			= fd_getgeo,
	.check_events		= floppy_check_events,
	.revalidate_disk	= floppy_revalidate,
#ifdef CONFIG_COMPAT
	.compat_ioctl		= fd_compat_ioctl,
#endif
};

/*