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
};

/*