Commit d2c5d4fc authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds
Browse files

[PATCH] cleanup cdrom_ioctl



Add a small helper for each ioctl to cut down cdrom_ioctl to a readable
size.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Cc: Acked-by: Jens Axboe <axboe@suse.de>
Signed-off-by: default avatarBenoit Boissinot <benoit.boissinot@ens-lyon.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2dd0ebcd
Loading
Loading
Loading
Loading
+537 −340
Original line number Diff line number Diff line
@@ -2196,82 +2196,89 @@ static int cdrom_read_cdda(struct cdrom_device_info *cdi, __u8 __user *ubuf,
	return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);	
}

/* Just about every imaginable ioctl is supported in the Uniform layer
 * these days. ATAPI / SCSI specific code now mainly resides in
 * mmc_ioct().
 */
int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
		struct inode *ip, unsigned int cmd, unsigned long arg)
static int cdrom_ioctl_multisession(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_device_ops *cdo = cdi->ops;
	struct cdrom_multisession ms_info;
	u8 requested_format;
	int ret;

	/* Try the generic SCSI command ioctl's first.. */
	ret = scsi_cmd_ioctl(file, ip->i_bdev->bd_disk, cmd, (void __user *)arg);
	if (ret != -ENOTTY)
		return ret;

	/* the first few commands do not deal with audio drive_info, but
	   only with routines in cdrom device operations. */
	switch (cmd) {
	case CDROMMULTISESSION: {
		struct cdrom_multisession ms_info;
		u_char requested_format;
	cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n");
                if (!(cdo->capability & CDC_MULTI_SESSION))

	if (!(cdi->ops->capability & CDC_MULTI_SESSION))
		return -ENOSYS;
		IOCTL_IN(arg, struct cdrom_multisession, ms_info);

	if (copy_from_user(&ms_info, argp, sizeof(ms_info)))
		return -EFAULT;

	requested_format = ms_info.addr_format;
		if (!((requested_format == CDROM_MSF) ||
			(requested_format == CDROM_LBA)))
	if (requested_format != CDROM_MSF && requested_format != CDROM_LBA)
		return -EINVAL;
	ms_info.addr_format = CDROM_LBA;
		if ((ret=cdo->get_last_session(cdi, &ms_info)))

	ret = cdi->ops->get_last_session(cdi, &ms_info);
	if (ret)
		return ret;
		sanitize_format(&ms_info.addr, &ms_info.addr_format,
				requested_format);
		IOCTL_OUT(arg, struct cdrom_multisession, ms_info);

	sanitize_format(&ms_info.addr, &ms_info.addr_format, requested_format);

	if (copy_to_user(argp, &ms_info, sizeof(ms_info)))
		return -EFAULT;

	cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION successful\n");
	return 0;
}

	case CDROMEJECT: {
static int cdrom_ioctl_eject(struct cdrom_device_info *cdi)
{
	cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n");

	if (!CDROM_CAN(CDC_OPEN_TRAY))
		return -ENOSYS;
	if (cdi->use_count != 1 || keeplocked)
		return -EBUSY;
		if (CDROM_CAN(CDC_LOCK))
			if ((ret=cdo->lock_door(cdi, 0)))
	if (CDROM_CAN(CDC_LOCK)) {
		int ret = cdi->ops->lock_door(cdi, 0);
		if (ret)
			return ret;
	}

		return cdo->tray_move(cdi, 1);
	return cdi->ops->tray_move(cdi, 1);
}

	case CDROMCLOSETRAY: {
static int cdrom_ioctl_closetray(struct cdrom_device_info *cdi)
{
	cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n");

	if (!CDROM_CAN(CDC_CLOSE_TRAY))
		return -ENOSYS;
		return cdo->tray_move(cdi, 0);
	return cdi->ops->tray_move(cdi, 0);
}

	case CDROMEJECT_SW: {
static int cdrom_ioctl_eject_sw(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n");

	if (!CDROM_CAN(CDC_OPEN_TRAY))
		return -ENOSYS;
	if (keeplocked)
		return -EBUSY;

	cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
	if (arg)
		cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
	return 0;
}

	case CDROM_MEDIA_CHANGED: {
static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	struct cdrom_changer_info *info;
		int changed;
	int ret;

	cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");

	if (!CDROM_CAN(CDC_MEDIA_CHANGED))
		return -ENOSYS;

@@ -2286,20 +2293,22 @@ int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
	if (!info)
		return -ENOMEM;

		if ((ret = cdrom_read_mech_status(cdi, info))) {
	ret = cdrom_read_mech_status(cdi, info);
	if (!ret)
		ret = info->slots[arg].change;
	kfree(info);
	return ret;
}

		changed = info->slots[arg].change;
		kfree(info);
		return changed;
		}

	case CDROM_SET_OPTIONS: {
static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n");
		/* options need to be in sync with capability. too late for
		   that, so we have to check each one separately... */

	/*
	 * Options need to be in sync with capability.
	 * Too late for that, so we have to check each one separately.
	 */
	switch (arg) {
	case CDO_USE_FFLAGS:
	case CDO_CHECK_TYPE:
@@ -2319,271 +2328,459 @@ int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
	return cdi->options;
}

	case CDROM_CLEAR_OPTIONS: {
static int cdrom_ioctl_clear_options(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n");

	cdi->options &= ~(int) arg;
	return cdi->options;
}

	case CDROM_SELECT_SPEED: {
static int cdrom_ioctl_select_speed(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n");

	if (!CDROM_CAN(CDC_SELECT_SPEED))
		return -ENOSYS;
		return cdo->select_speed(cdi, arg);
	return cdi->ops->select_speed(cdi, arg);
}

	case CDROM_SELECT_DISC: {
static int cdrom_ioctl_select_disc(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n");

	if (!CDROM_CAN(CDC_SELECT_DISC))
		return -ENOSYS;

                if ((arg != CDSL_CURRENT) && (arg != CDSL_NONE))
	if (arg != CDSL_CURRENT && arg != CDSL_NONE) {
		if ((int)arg >= cdi->capacity)
			return -EINVAL;
	}

		/* cdo->select_disc is a hook to allow a driver-specific
		 * way of seleting disc.  However, since there is no
		 * equiv hook for cdrom_slot_status this may not 
		 * actually be useful...
	/*
	 * ->select_disc is a hook to allow a driver-specific way of
	 * seleting disc.  However, since there is no equivalent hook for
	 * cdrom_slot_status this may not actually be useful...
	 */
		if (cdo->select_disc != NULL)
			return cdo->select_disc(cdi, arg);
	if (cdi->ops->select_disc)
		return cdi->ops->select_disc(cdi, arg);

		/* no driver specific select_disc(), call our own */
	cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n");
	return cdrom_select_disc(cdi, arg);
}

	case CDROMRESET: {
static int cdrom_ioctl_reset(struct cdrom_device_info *cdi,
		struct block_device *bdev)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
		cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
	if (!CDROM_CAN(CDC_RESET))
		return -ENOSYS;
		invalidate_bdev(ip->i_bdev, 0);
		return cdo->reset(cdi);
	invalidate_bdev(bdev, 0);
	return cdi->ops->reset(cdi);
}

	case CDROM_LOCKDOOR: {
static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl");

	if (!CDROM_CAN(CDC_LOCK))
		return -EDRIVE_CANT_DO_THIS;

	keeplocked = arg ? 1 : 0;
		/* don't unlock the door on multiple opens,but allow root
		 * to do so */
		if ((cdi->use_count != 1) && !arg && !capable(CAP_SYS_ADMIN))

	/*
	 * Don't unlock the door on multiple opens by default, but allow
	 * root to do so.
	 */
	if (cdi->use_count != 1 && !arg && !capable(CAP_SYS_ADMIN))
		return -EBUSY;
		return cdo->lock_door(cdi, arg);
	return cdi->ops->lock_door(cdi, arg);
}

	case CDROM_DEBUG: {
static int cdrom_ioctl_debug(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
		cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");
	debug = arg ? 1 : 0;
	return debug;
}

	case CDROM_GET_CAPABILITY: {
static int cdrom_ioctl_get_capability(struct cdrom_device_info *cdi)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
		return (cdo->capability & ~cdi->mask);
	return (cdi->ops->capability & ~cdi->mask);
}

/* The following function is implemented, although very few audio
/*
 * The following function is implemented, although very few audio
 * discs give Universal Product Code information, which should just be
 * the Medium Catalog Number on the box.  Note, that the way the code
 * is written on the CD is /not/ uniform across all discs!
 */
	case CDROM_GET_MCN: {
static int cdrom_ioctl_get_mcn(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_mcn mcn;
	int ret;

	cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n");
		if (!(cdo->capability & CDC_MCN))

	if (!(cdi->ops->capability & CDC_MCN))
		return -ENOSYS;
		if ((ret=cdo->get_mcn(cdi, &mcn)))
	ret = cdi->ops->get_mcn(cdi, &mcn);
	if (ret)
		return ret;
		IOCTL_OUT(arg, struct cdrom_mcn, mcn);

	if (copy_to_user(argp, &mcn, sizeof(mcn)))
		return -EFAULT;
	cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n");
	return 0;
}

	case CDROM_DRIVE_STATUS: {
static int cdrom_ioctl_drive_status(struct cdrom_device_info *cdi,
		unsigned long arg)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n");
		if (!(cdo->capability & CDC_DRIVE_STATUS))

	if (!(cdi->ops->capability & CDC_DRIVE_STATUS))
		return -ENOSYS;
		if (!CDROM_CAN(CDC_SELECT_DISC))
			return cdo->drive_status(cdi, CDSL_CURRENT);
                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) 
			return cdo->drive_status(cdi, CDSL_CURRENT);
	if (!CDROM_CAN(CDC_SELECT_DISC) ||
	    (arg == CDSL_CURRENT || arg == CDSL_NONE))
		return cdi->ops->drive_status(cdi, CDSL_CURRENT);
	if (((int)arg >= cdi->capacity))
		return -EINVAL;
	return cdrom_slot_status(cdi, arg);
}

	/* Ok, this is where problems start.  The current interface for the
	   CDROM_DISC_STATUS ioctl is flawed.  It makes the false assumption
	   that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc.  Unfortunatly,
	   while this is often the case, it is also very common for CDs to
	   have some tracks with data, and some tracks with audio.  Just 
	   because I feel like it, I declare the following to be the best
	   way to cope.  If the CD has ANY data tracks on it, it will be
	   returned as a data CD.  If it has any XA tracks, I will return
	   it as that.  Now I could simplify this interface by combining these 
	   returns with the above, but this more clearly demonstrates
	   the problem with the current interface.  Too bad this wasn't 
	   designed to use bitmasks...         -Erik 

	   Well, now we have the option CDS_MIXED: a mixed-type CD. 
	   User level programmers might feel the ioctl is not very useful.
	   					---david
/*
 * Ok, this is where problems start.  The current interface for the
 * CDROM_DISC_STATUS ioctl is flawed.  It makes the false assumption that
 * CDs are all CDS_DATA_1 or all CDS_AUDIO, etc.  Unfortunatly, while this
 * is often the case, it is also very common for CDs to have some tracks
 * with data, and some tracks with audio.  Just because I feel like it,
 * I declare the following to be the best way to cope.  If the CD has ANY
 * data tracks on it, it will be returned as a data CD.  If it has any XA
 * tracks, I will return it as that.  Now I could simplify this interface
 * by combining these  returns with the above, but this more clearly
 * demonstrates the problem with the current interface.  Too bad this
 * wasn't designed to use bitmasks...         -Erik
 *
 * Well, now we have the option CDS_MIXED: a mixed-type CD.
 * User level programmers might feel the ioctl is not very useful.
 *					---david
 */
	case CDROM_DISC_STATUS: {
static int cdrom_ioctl_disc_status(struct cdrom_device_info *cdi)
{
	tracktype tracks;

	cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n");

	cdrom_count_tracks(cdi, &tracks);
	if (tracks.error)
			return(tracks.error);
		return tracks.error;

	/* Policy mode on */
	if (tracks.audio > 0) {
			if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) 
		if (!tracks.data && !tracks.cdi && !tracks.xa)
			return CDS_AUDIO;
		else
			return CDS_MIXED;
	}
		if (tracks.cdi > 0) return CDS_XA_2_2;
		if (tracks.xa > 0) return CDS_XA_2_1;
		if (tracks.data > 0) return CDS_DATA_1;

	if (tracks.cdi > 0)
		return CDS_XA_2_2;
	if (tracks.xa > 0)
		return CDS_XA_2_1;
	if (tracks.data > 0)
		return CDS_DATA_1;
	/* Policy mode off */

	cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n");
	return CDS_NO_INFO;
}

	case CDROM_CHANGER_NSLOTS: {
static int cdrom_ioctl_changer_nslots(struct cdrom_device_info *cdi)
{
	cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n");
	return cdi->capacity;
}
	}

	/* use the ioctls that are implemented through the generic_packet()
	   interface. this may look at bit funny, but if -ENOTTY is
	   returned that particular ioctl is not implemented and we
	   let it go through the device specific ones. */
	if (CDROM_CAN(CDC_GENERIC_PACKET)) {
		ret = mmc_ioctl(cdi, cmd, arg);
		if (ret != -ENOTTY) {
			return ret;
		}
	}

	/* note: most of the cdinfo() calls are commented out here,
	   because they fill up the sys log when CD players poll
	   the drive. */
	switch (cmd) {
	case CDROMSUBCHNL: {
static int cdrom_ioctl_get_subchnl(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_subchnl q;
		u_char requested, back;
	u8 requested, back;
	int ret;

	/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
		/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ 
		IOCTL_IN(arg, struct cdrom_subchnl, q);
	if (copy_from_user(&q, argp, sizeof(q)))
		return -EFAULT;

	requested = q.cdsc_format;
		if (!((requested == CDROM_MSF) ||
		      (requested == CDROM_LBA)))
	if (requested != CDROM_MSF && requested != CDROM_LBA)
		return -EINVAL;
	q.cdsc_format = CDROM_MSF;
		if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))

	ret = cdi->ops->audio_ioctl(cdi, CDROMSUBCHNL, &q);
	if (ret)
		return ret;

	back = q.cdsc_format; /* local copy */
	sanitize_format(&q.cdsc_absaddr, &back, requested);
	sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
		IOCTL_OUT(arg, struct cdrom_subchnl, q);

	if (copy_to_user(argp, &q, sizeof(q)))
		return -EFAULT;
	/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
	return 0;
}
	case CDROMREADTOCHDR: {

static int cdrom_ioctl_read_tochdr(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_tochdr header;
	int ret;

	/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ 
		IOCTL_IN(arg, struct cdrom_tochdr, header);
		if ((ret=cdo->audio_ioctl(cdi, cmd, &header)))
	if (copy_from_user(&header, argp, sizeof(header)))
		return -EFAULT;

	ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header);
	if (ret)
		return ret;
		IOCTL_OUT(arg, struct cdrom_tochdr, header);

	if (copy_to_user(argp, &header, sizeof(header)))
		return -EFAULT;
	/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */
	return 0;
}
	case CDROMREADTOCENTRY: {

static int cdrom_ioctl_read_tocentry(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_tocentry entry;
		u_char requested_format;
	u8 requested_format;
	int ret;

	/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ 
		IOCTL_IN(arg, struct cdrom_tocentry, entry);
	if (copy_from_user(&entry, argp, sizeof(entry)))
		return -EFAULT;

	requested_format = entry.cdte_format;
		if (!((requested_format == CDROM_MSF) || 
			(requested_format == CDROM_LBA)))
	if (requested_format != CDROM_MSF && requested_format != CDROM_LBA)
		return -EINVAL;
	/* make interface to low-level uniform */
	entry.cdte_format = CDROM_MSF;
		if ((ret=cdo->audio_ioctl(cdi, cmd, &entry)))
	ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry);
	if (ret)
		return ret;
		sanitize_format(&entry.cdte_addr,
		&entry.cdte_format, requested_format);
		IOCTL_OUT(arg, struct cdrom_tocentry, entry);
	sanitize_format(&entry.cdte_addr, &entry.cdte_format, requested_format);

	if (copy_to_user(argp, &entry, sizeof(entry)))
		return -EFAULT;
	/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */
	return 0;
}
	case CDROMPLAYMSF: {

static int cdrom_ioctl_play_msf(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_msf msf;

	cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); 
		IOCTL_IN(arg, struct cdrom_msf, msf);
		return cdo->audio_ioctl(cdi, cmd, &msf);
	if (copy_from_user(&msf, argp, sizeof(msf)))
		return -EFAULT;
	return cdi->ops->audio_ioctl(cdi, CDROMPLAYMSF, &msf);
}
	case CDROMPLAYTRKIND: {

static int cdrom_ioctl_play_trkind(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_ti ti;
	int ret;

	cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n");

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); 
		IOCTL_IN(arg, struct cdrom_ti, ti);
		CHECKAUDIO;
		return cdo->audio_ioctl(cdi, cmd, &ti);
	if (copy_from_user(&ti, argp, sizeof(ti)))
		return -EFAULT;

	ret = check_for_audio_disc(cdi, cdi->ops);
	if (ret)
		return ret;
	return cdi->ops->audio_ioctl(cdi, CDROMPLAYTRKIND, &ti);
}
	case CDROMVOLCTRL: {
static int cdrom_ioctl_volctrl(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_volctrl volume;

	cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n");

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); 
		IOCTL_IN(arg, struct cdrom_volctrl, volume);
		return cdo->audio_ioctl(cdi, cmd, &volume);
	if (copy_from_user(&volume, argp, sizeof(volume)))
		return -EFAULT;
	return cdi->ops->audio_ioctl(cdi, CDROMVOLCTRL, &volume);
}
	case CDROMVOLREAD: {

static int cdrom_ioctl_volread(struct cdrom_device_info *cdi,
		void __user *argp)
{
	struct cdrom_volctrl volume;
	int ret;

	cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n");

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); 
		if ((ret=cdo->audio_ioctl(cdi, cmd, &volume)))

	ret = cdi->ops->audio_ioctl(cdi, CDROMVOLREAD, &volume);
	if (ret)
		return ret;
		IOCTL_OUT(arg, struct cdrom_volctrl, volume);

	if (copy_to_user(argp, &volume, sizeof(volume)))
		return -EFAULT;
	return 0;
}

static int cdrom_ioctl_audioctl(struct cdrom_device_info *cdi,
		unsigned int cmd)
{
	int ret;

	cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n");

	if (!CDROM_CAN(CDC_PLAY_AUDIO))
		return -ENOSYS;
	ret = check_for_audio_disc(cdi, cdi->ops);
	if (ret)
		return ret;
	return cdi->ops->audio_ioctl(cdi, cmd, NULL);
}

/*
 * Just about every imaginable ioctl is supported in the Uniform layer
 * these days.
 * ATAPI / SCSI specific code now mainly resides in mmc_ioctl().
 */
int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
		struct inode *ip, unsigned int cmd, unsigned long arg)
{
	void __user *argp = (void __user *)arg;
	int ret;

	/*
	 * Try the generic SCSI command ioctl's first.
	 */
	ret = scsi_cmd_ioctl(file, ip->i_bdev->bd_disk, cmd, argp);
	if (ret != -ENOTTY)
		return ret;

	switch (cmd) {
	case CDROMMULTISESSION:
		return cdrom_ioctl_multisession(cdi, argp);
	case CDROMEJECT:
		return cdrom_ioctl_eject(cdi);
	case CDROMCLOSETRAY:
		return cdrom_ioctl_closetray(cdi);
	case CDROMEJECT_SW:
		return cdrom_ioctl_eject_sw(cdi, arg);
	case CDROM_MEDIA_CHANGED:
		return cdrom_ioctl_media_changed(cdi, arg);
	case CDROM_SET_OPTIONS:
		return cdrom_ioctl_set_options(cdi, arg);
	case CDROM_CLEAR_OPTIONS:
		return cdrom_ioctl_clear_options(cdi, arg);
	case CDROM_SELECT_SPEED:
		return cdrom_ioctl_select_speed(cdi, arg);
	case CDROM_SELECT_DISC:
		return cdrom_ioctl_select_disc(cdi, arg);
	case CDROMRESET:
		return cdrom_ioctl_reset(cdi, ip->i_bdev);
	case CDROM_LOCKDOOR:
		return cdrom_ioctl_lock_door(cdi, arg);
	case CDROM_DEBUG:
		return cdrom_ioctl_debug(cdi, arg);
	case CDROM_GET_CAPABILITY:
		return cdrom_ioctl_get_capability(cdi);
	case CDROM_GET_MCN:
		return cdrom_ioctl_get_mcn(cdi, argp);
	case CDROM_DRIVE_STATUS:
		return cdrom_ioctl_drive_status(cdi, arg);
	case CDROM_DISC_STATUS:
		return cdrom_ioctl_disc_status(cdi);
	case CDROM_CHANGER_NSLOTS:
		return cdrom_ioctl_changer_nslots(cdi);
	}

	/*
	 * Use the ioctls that are implemented through the generic_packet()
	 * interface. this may look at bit funny, but if -ENOTTY is
	 * returned that particular ioctl is not implemented and we
	 * let it go through the device specific ones.
	 */
	if (CDROM_CAN(CDC_GENERIC_PACKET)) {
		ret = mmc_ioctl(cdi, cmd, arg);
		if (ret != -ENOTTY)
			return ret;
	}

	/*
	 * Note: most of the cdinfo() calls are commented out here,
	 * because they fill up the sys log when CD players poll
	 * the drive.
	 */
	switch (cmd) {
	case CDROMSUBCHNL:
		return cdrom_ioctl_get_subchnl(cdi, argp);
	case CDROMREADTOCHDR:
		return cdrom_ioctl_read_tochdr(cdi, argp);
	case CDROMREADTOCENTRY:
		return cdrom_ioctl_read_tocentry(cdi, argp);
	case CDROMPLAYMSF:
		return cdrom_ioctl_play_msf(cdi, argp);
	case CDROMPLAYTRKIND:
		return cdrom_ioctl_play_trkind(cdi, argp);
	case CDROMVOLCTRL:
		return cdrom_ioctl_volctrl(cdi, argp);
	case CDROMVOLREAD:
		return cdrom_ioctl_volread(cdi, argp);
	case CDROMSTART:
	case CDROMSTOP:
	case CDROMPAUSE:
	case CDROMRESUME: {
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); 
		CHECKAUDIO;
		return cdo->audio_ioctl(cdi, cmd, NULL);
	case CDROMRESUME:
		return cdrom_ioctl_audioctl(cdi, cmd);
	}
	} /* switch */

	/* do the device specific ioctls */
	/*
	 * Finally, do the device specific ioctls
	 */
	if (CDROM_CAN(CDC_IOCTLS))
		return cdo->dev_ioctl(cdi, cmd, arg);
		return cdi->ops->dev_ioctl(cdi, cmd, arg);

	return -ENOSYS;
}