[PATCH] cleanup cdrom_ioctl
Add a small helper for each ioctl to cut down cdrom_ioctl to a readable size. Signed-off-by: Christoph Hellwig <hch@lst.de> Cc: Acked-by: Jens Axboe <axboe@suse.de> Signed-off-by: Benoit Boissinot <benoit.boissinot@ens-lyon.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
2dd0ebcd2a
commit
d2c5d4fc07
1 changed files with 538 additions and 341 deletions
|
@ -2196,395 +2196,592 @@ static int cdrom_read_cdda(struct cdrom_device_info *cdi, __u8 __user *ubuf,
|
||||||
return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);
|
return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Just about every imaginable ioctl is supported in the Uniform layer
|
static int cdrom_ioctl_multisession(struct cdrom_device_info *cdi,
|
||||||
* these days. ATAPI / SCSI specific code now mainly resides in
|
void __user *argp)
|
||||||
* mmc_ioct().
|
|
||||||
*/
|
|
||||||
int cdrom_ioctl(struct file * file, struct cdrom_device_info *cdi,
|
|
||||||
struct inode *ip, unsigned int cmd, unsigned long arg)
|
|
||||||
{
|
{
|
||||||
struct cdrom_device_ops *cdo = cdi->ops;
|
struct cdrom_multisession ms_info;
|
||||||
|
u8 requested_format;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Try the generic SCSI command ioctl's first.. */
|
cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n");
|
||||||
ret = scsi_cmd_ioctl(file, ip->i_bdev->bd_disk, cmd, (void __user *)arg);
|
|
||||||
if (ret != -ENOTTY)
|
if (!(cdi->ops->capability & CDC_MULTI_SESSION))
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
|
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)
|
||||||
|
return -EINVAL;
|
||||||
|
ms_info.addr_format = CDROM_LBA;
|
||||||
|
|
||||||
|
ret = cdi->ops->get_last_session(cdi, &ms_info);
|
||||||
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* the first few commands do not deal with audio drive_info, but
|
sanitize_format(&ms_info.addr, &ms_info.addr_format, requested_format);
|
||||||
only with routines in cdrom device operations. */
|
|
||||||
switch (cmd) {
|
if (copy_to_user(argp, &ms_info, sizeof(ms_info)))
|
||||||
case CDROMMULTISESSION: {
|
return -EFAULT;
|
||||||
struct cdrom_multisession ms_info;
|
|
||||||
u_char requested_format;
|
cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION successful\n");
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n");
|
return 0;
|
||||||
if (!(cdo->capability & CDC_MULTI_SESSION))
|
}
|
||||||
return -ENOSYS;
|
|
||||||
IOCTL_IN(arg, struct cdrom_multisession, ms_info);
|
static int cdrom_ioctl_eject(struct cdrom_device_info *cdi)
|
||||||
requested_format = ms_info.addr_format;
|
{
|
||||||
if (!((requested_format == CDROM_MSF) ||
|
cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n");
|
||||||
(requested_format == CDROM_LBA)))
|
|
||||||
return -EINVAL;
|
if (!CDROM_CAN(CDC_OPEN_TRAY))
|
||||||
ms_info.addr_format = CDROM_LBA;
|
return -ENOSYS;
|
||||||
if ((ret=cdo->get_last_session(cdi, &ms_info)))
|
if (cdi->use_count != 1 || keeplocked)
|
||||||
|
return -EBUSY;
|
||||||
|
if (CDROM_CAN(CDC_LOCK)) {
|
||||||
|
int ret = cdi->ops->lock_door(cdi, 0);
|
||||||
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
sanitize_format(&ms_info.addr, &ms_info.addr_format,
|
}
|
||||||
requested_format);
|
|
||||||
IOCTL_OUT(arg, struct cdrom_multisession, ms_info);
|
|
||||||
cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION successful\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROMEJECT: {
|
return cdi->ops->tray_move(cdi, 1);
|
||||||
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)))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return cdo->tray_move(cdi, 1);
|
static int cdrom_ioctl_closetray(struct cdrom_device_info *cdi)
|
||||||
}
|
{
|
||||||
|
cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n");
|
||||||
|
|
||||||
case CDROMCLOSETRAY: {
|
if (!CDROM_CAN(CDC_CLOSE_TRAY))
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n");
|
return -ENOSYS;
|
||||||
if (!CDROM_CAN(CDC_CLOSE_TRAY))
|
return cdi->ops->tray_move(cdi, 0);
|
||||||
return -ENOSYS;
|
}
|
||||||
return cdo->tray_move(cdi, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROMEJECT_SW: {
|
static int cdrom_ioctl_eject_sw(struct cdrom_device_info *cdi,
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n");
|
unsigned long arg)
|
||||||
if (!CDROM_CAN(CDC_OPEN_TRAY))
|
{
|
||||||
return -ENOSYS;
|
cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n");
|
||||||
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: {
|
if (!CDROM_CAN(CDC_OPEN_TRAY))
|
||||||
struct cdrom_changer_info *info;
|
return -ENOSYS;
|
||||||
int changed;
|
if (keeplocked)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");
|
cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
|
||||||
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
|
if (arg)
|
||||||
return -ENOSYS;
|
cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* cannot select disc or select current disc */
|
static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi,
|
||||||
if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
|
unsigned long arg)
|
||||||
return media_changed(cdi, 1);
|
{
|
||||||
|
struct cdrom_changer_info *info;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if ((unsigned int)arg >= cdi->capacity)
|
cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
|
||||||
if (!info)
|
return -ENOSYS;
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if ((ret = cdrom_read_mech_status(cdi, info))) {
|
/* cannot select disc or select current disc */
|
||||||
kfree(info);
|
if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
|
||||||
return ret;
|
return media_changed(cdi, 1);
|
||||||
}
|
|
||||||
|
|
||||||
changed = info->slots[arg].change;
|
if ((unsigned int)arg >= cdi->capacity)
|
||||||
kfree(info);
|
return -EINVAL;
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROM_SET_OPTIONS: {
|
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n");
|
if (!info)
|
||||||
/* options need to be in sync with capability. too late for
|
return -ENOMEM;
|
||||||
that, so we have to check each one separately... */
|
|
||||||
switch (arg) {
|
|
||||||
case CDO_USE_FFLAGS:
|
|
||||||
case CDO_CHECK_TYPE:
|
|
||||||
break;
|
|
||||||
case CDO_LOCK:
|
|
||||||
if (!CDROM_CAN(CDC_LOCK))
|
|
||||||
return -ENOSYS;
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
return cdi->options;
|
|
||||||
/* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */
|
|
||||||
default:
|
|
||||||
if (!CDROM_CAN(arg))
|
|
||||||
return -ENOSYS;
|
|
||||||
}
|
|
||||||
cdi->options |= (int) arg;
|
|
||||||
return cdi->options;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROM_CLEAR_OPTIONS: {
|
ret = cdrom_read_mech_status(cdi, info);
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n");
|
if (!ret)
|
||||||
cdi->options &= ~(int) arg;
|
ret = info->slots[arg].change;
|
||||||
return cdi->options;
|
kfree(info);
|
||||||
}
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
case CDROM_SELECT_SPEED: {
|
static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi,
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n");
|
unsigned long arg)
|
||||||
if (!CDROM_CAN(CDC_SELECT_SPEED))
|
{
|
||||||
return -ENOSYS;
|
cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n");
|
||||||
return cdo->select_speed(cdi, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROM_SELECT_DISC: {
|
/*
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n");
|
* Options need to be in sync with capability.
|
||||||
if (!CDROM_CAN(CDC_SELECT_DISC))
|
* Too late for that, so we have to check each one separately.
|
||||||
return -ENOSYS;
|
*/
|
||||||
|
switch (arg) {
|
||||||
if ((arg != CDSL_CURRENT) && (arg != CDSL_NONE))
|
case CDO_USE_FFLAGS:
|
||||||
if ((int)arg >= cdi->capacity)
|
case CDO_CHECK_TYPE:
|
||||||
return -EINVAL;
|
break;
|
||||||
|
case CDO_LOCK:
|
||||||
/* 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...
|
|
||||||
*/
|
|
||||||
if (cdo->select_disc != NULL)
|
|
||||||
return cdo->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: {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROM_LOCKDOOR: {
|
|
||||||
cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl");
|
|
||||||
if (!CDROM_CAN(CDC_LOCK))
|
if (!CDROM_CAN(CDC_LOCK))
|
||||||
return -EDRIVE_CANT_DO_THIS;
|
return -ENOSYS;
|
||||||
keeplocked = arg ? 1 : 0;
|
break;
|
||||||
/* don't unlock the door on multiple opens,but allow root
|
case 0:
|
||||||
* to do so */
|
return cdi->options;
|
||||||
if ((cdi->use_count != 1) && !arg && !capable(CAP_SYS_ADMIN))
|
/* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */
|
||||||
return -EBUSY;
|
default:
|
||||||
return cdo->lock_door(cdi, arg);
|
if (!CDROM_CAN(arg))
|
||||||
}
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
cdi->options |= (int) arg;
|
||||||
|
return cdi->options;
|
||||||
|
}
|
||||||
|
|
||||||
case CDROM_DEBUG: {
|
static int cdrom_ioctl_clear_options(struct cdrom_device_info *cdi,
|
||||||
if (!capable(CAP_SYS_ADMIN))
|
unsigned long arg)
|
||||||
return -EACCES;
|
{
|
||||||
cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");
|
cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n");
|
||||||
debug = arg ? 1 : 0;
|
|
||||||
return debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROM_GET_CAPABILITY: {
|
cdi->options &= ~(int) arg;
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
|
return cdi->options;
|
||||||
return (cdo->capability & ~cdi->mask);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* The following function is implemented, although very few audio
|
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 cdi->ops->select_speed(cdi, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ((int)arg >= cdi->capacity)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ->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 (cdi->ops->select_disc)
|
||||||
|
return cdi->ops->select_disc(cdi, arg);
|
||||||
|
|
||||||
|
cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n");
|
||||||
|
return cdrom_select_disc(cdi, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (!CDROM_CAN(CDC_RESET))
|
||||||
|
return -ENOSYS;
|
||||||
|
invalidate_bdev(bdev, 0);
|
||||||
|
return cdi->ops->reset(cdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 by default, but allow
|
||||||
|
* root to do so.
|
||||||
|
*/
|
||||||
|
if (cdi->use_count != 1 && !arg && !capable(CAP_SYS_ADMIN))
|
||||||
|
return -EBUSY;
|
||||||
|
return cdi->ops->lock_door(cdi, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
debug = arg ? 1 : 0;
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cdrom_ioctl_get_capability(struct cdrom_device_info *cdi)
|
||||||
|
{
|
||||||
|
cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
|
||||||
|
return (cdi->ops->capability & ~cdi->mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following function is implemented, although very few audio
|
||||||
* discs give Universal Product Code information, which should just be
|
* discs give Universal Product Code information, which should just be
|
||||||
* the Medium Catalog Number on the box. Note, that the way the code
|
* the Medium Catalog Number on the box. Note, that the way the code
|
||||||
* is written on the CD is /not/ uniform across all discs!
|
* 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,
|
||||||
struct cdrom_mcn mcn;
|
void __user *argp)
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n");
|
{
|
||||||
if (!(cdo->capability & CDC_MCN))
|
struct cdrom_mcn mcn;
|
||||||
return -ENOSYS;
|
int ret;
|
||||||
if ((ret=cdo->get_mcn(cdi, &mcn)))
|
|
||||||
return ret;
|
|
||||||
IOCTL_OUT(arg, struct cdrom_mcn, mcn);
|
|
||||||
cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CDROM_DRIVE_STATUS: {
|
cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n");
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n");
|
|
||||||
if (!(cdo->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 (((int)arg >= cdi->capacity))
|
|
||||||
return -EINVAL;
|
|
||||||
return cdrom_slot_status(cdi, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ok, this is where problems start. The current interface for the
|
if (!(cdi->ops->capability & CDC_MCN))
|
||||||
CDROM_DISC_STATUS ioctl is flawed. It makes the false assumption
|
return -ENOSYS;
|
||||||
that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc. Unfortunatly,
|
ret = cdi->ops->get_mcn(cdi, &mcn);
|
||||||
while this is often the case, it is also very common for CDs to
|
if (ret)
|
||||||
have some tracks with data, and some tracks with audio. Just
|
return ret;
|
||||||
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.
|
if (copy_to_user(argp, &mcn, sizeof(mcn)))
|
||||||
User level programmers might feel the ioctl is not very useful.
|
return -EFAULT;
|
||||||
---david
|
cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n");
|
||||||
*/
|
return 0;
|
||||||
case CDROM_DISC_STATUS: {
|
}
|
||||||
tracktype tracks;
|
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n");
|
|
||||||
cdrom_count_tracks(cdi, &tracks);
|
|
||||||
if (tracks.error)
|
|
||||||
return(tracks.error);
|
|
||||||
|
|
||||||
/* Policy mode on */
|
static int cdrom_ioctl_drive_status(struct cdrom_device_info *cdi,
|
||||||
if (tracks.audio > 0) {
|
unsigned long arg)
|
||||||
if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0)
|
{
|
||||||
return CDS_AUDIO;
|
cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n");
|
||||||
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;
|
|
||||||
/* Policy mode off */
|
|
||||||
|
|
||||||
cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n");
|
if (!(cdi->ops->capability & CDC_DRIVE_STATUS))
|
||||||
return CDS_NO_INFO;
|
return -ENOSYS;
|
||||||
}
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
case CDROM_CHANGER_NSLOTS: {
|
/*
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n");
|
* Ok, this is where problems start. The current interface for the
|
||||||
return cdi->capacity;
|
* 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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Policy mode on */
|
||||||
|
if (tracks.audio > 0) {
|
||||||
|
if (!tracks.data && !tracks.cdi && !tracks.xa)
|
||||||
|
return CDS_AUDIO;
|
||||||
|
else
|
||||||
|
return CDS_MIXED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use the ioctls that are implemented through the generic_packet()
|
if (tracks.cdi > 0)
|
||||||
interface. this may look at bit funny, but if -ENOTTY is
|
return CDS_XA_2_2;
|
||||||
returned that particular ioctl is not implemented and we
|
if (tracks.xa > 0)
|
||||||
let it go through the device specific ones. */
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cdrom_ioctl_changer_nslots(struct cdrom_device_info *cdi)
|
||||||
|
{
|
||||||
|
cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n");
|
||||||
|
return cdi->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cdrom_ioctl_get_subchnl(struct cdrom_device_info *cdi,
|
||||||
|
void __user *argp)
|
||||||
|
{
|
||||||
|
struct cdrom_subchnl q;
|
||||||
|
u8 requested, back;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/
|
||||||
|
|
||||||
|
if (!CDROM_CAN(CDC_PLAY_AUDIO))
|
||||||
|
return -ENOSYS;
|
||||||
|
if (copy_from_user(&q, argp, sizeof(q)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
requested = q.cdsc_format;
|
||||||
|
if (requested != CDROM_MSF && requested != CDROM_LBA)
|
||||||
|
return -EINVAL;
|
||||||
|
q.cdsc_format = CDROM_MSF;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (copy_to_user(argp, &q, sizeof(q)))
|
||||||
|
return -EFAULT;
|
||||||
|
/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (copy_from_user(&header, argp, sizeof(header)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (copy_to_user(argp, &header, sizeof(header)))
|
||||||
|
return -EFAULT;
|
||||||
|
/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cdrom_ioctl_read_tocentry(struct cdrom_device_info *cdi,
|
||||||
|
void __user *argp)
|
||||||
|
{
|
||||||
|
struct cdrom_tocentry entry;
|
||||||
|
u8 requested_format;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */
|
||||||
|
|
||||||
|
if (!CDROM_CAN(CDC_PLAY_AUDIO))
|
||||||
|
return -ENOSYS;
|
||||||
|
if (copy_from_user(&entry, argp, sizeof(entry)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
requested_format = entry.cdte_format;
|
||||||
|
if (requested_format != CDROM_MSF && requested_format != CDROM_LBA)
|
||||||
|
return -EINVAL;
|
||||||
|
/* make interface to low-level uniform */
|
||||||
|
entry.cdte_format = CDROM_MSF;
|
||||||
|
ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (copy_from_user(&msf, argp, sizeof(msf)))
|
||||||
|
return -EFAULT;
|
||||||
|
return cdi->ops->audio_ioctl(cdi, CDROMPLAYMSF, &msf);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
if (copy_from_user(&volume, argp, sizeof(volume)))
|
||||||
|
return -EFAULT;
|
||||||
|
return cdi->ops->audio_ioctl(cdi, CDROMVOLCTRL, &volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
ret = cdi->ops->audio_ioctl(cdi, CDROMVOLREAD, &volume);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
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)) {
|
if (CDROM_CAN(CDC_GENERIC_PACKET)) {
|
||||||
ret = mmc_ioctl(cdi, cmd, arg);
|
ret = mmc_ioctl(cdi, cmd, arg);
|
||||||
if (ret != -ENOTTY) {
|
if (ret != -ENOTTY)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* note: most of the cdinfo() calls are commented out here,
|
/*
|
||||||
because they fill up the sys log when CD players poll
|
* Note: most of the cdinfo() calls are commented out here,
|
||||||
the drive. */
|
* because they fill up the sys log when CD players poll
|
||||||
|
* the drive.
|
||||||
|
*/
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CDROMSUBCHNL: {
|
case CDROMSUBCHNL:
|
||||||
struct cdrom_subchnl q;
|
return cdrom_ioctl_get_subchnl(cdi, argp);
|
||||||
u_char requested, back;
|
case CDROMREADTOCHDR:
|
||||||
if (!CDROM_CAN(CDC_PLAY_AUDIO))
|
return cdrom_ioctl_read_tochdr(cdi, argp);
|
||||||
return -ENOSYS;
|
case CDROMREADTOCENTRY:
|
||||||
/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/
|
return cdrom_ioctl_read_tocentry(cdi, argp);
|
||||||
IOCTL_IN(arg, struct cdrom_subchnl, q);
|
case CDROMPLAYMSF:
|
||||||
requested = q.cdsc_format;
|
return cdrom_ioctl_play_msf(cdi, argp);
|
||||||
if (!((requested == CDROM_MSF) ||
|
case CDROMPLAYTRKIND:
|
||||||
(requested == CDROM_LBA)))
|
return cdrom_ioctl_play_trkind(cdi, argp);
|
||||||
return -EINVAL;
|
case CDROMVOLCTRL:
|
||||||
q.cdsc_format = CDROM_MSF;
|
return cdrom_ioctl_volctrl(cdi, argp);
|
||||||
if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))
|
case CDROMVOLREAD:
|
||||||
return ret;
|
return cdrom_ioctl_volread(cdi, argp);
|
||||||
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);
|
|
||||||
/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case CDROMREADTOCHDR: {
|
|
||||||
struct cdrom_tochdr header;
|
|
||||||
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)))
|
|
||||||
return ret;
|
|
||||||
IOCTL_OUT(arg, struct cdrom_tochdr, header);
|
|
||||||
/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case CDROMREADTOCENTRY: {
|
|
||||||
struct cdrom_tocentry entry;
|
|
||||||
u_char requested_format;
|
|
||||||
if (!CDROM_CAN(CDC_PLAY_AUDIO))
|
|
||||||
return -ENOSYS;
|
|
||||||
/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */
|
|
||||||
IOCTL_IN(arg, struct cdrom_tocentry, entry);
|
|
||||||
requested_format = entry.cdte_format;
|
|
||||||
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)))
|
|
||||||
return ret;
|
|
||||||
sanitize_format(&entry.cdte_addr,
|
|
||||||
&entry.cdte_format, requested_format);
|
|
||||||
IOCTL_OUT(arg, struct cdrom_tocentry, entry);
|
|
||||||
/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case CDROMPLAYMSF: {
|
|
||||||
struct cdrom_msf msf;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
case CDROMPLAYTRKIND: {
|
|
||||||
struct cdrom_ti ti;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
case CDROMVOLCTRL: {
|
|
||||||
struct cdrom_volctrl volume;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
case CDROMVOLREAD: {
|
|
||||||
struct cdrom_volctrl volume;
|
|
||||||
if (!CDROM_CAN(CDC_PLAY_AUDIO))
|
|
||||||
return -ENOSYS;
|
|
||||||
cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n");
|
|
||||||
if ((ret=cdo->audio_ioctl(cdi, cmd, &volume)))
|
|
||||||
return ret;
|
|
||||||
IOCTL_OUT(arg, struct cdrom_volctrl, volume);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case CDROMSTART:
|
case CDROMSTART:
|
||||||
case CDROMSTOP:
|
case CDROMSTOP:
|
||||||
case CDROMPAUSE:
|
case CDROMPAUSE:
|
||||||
case CDROMRESUME: {
|
case CDROMRESUME:
|
||||||
if (!CDROM_CAN(CDC_PLAY_AUDIO))
|
return cdrom_ioctl_audioctl(cdi, cmd);
|
||||||
return -ENOSYS;
|
}
|
||||||
cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n");
|
|
||||||
CHECKAUDIO;
|
|
||||||
return cdo->audio_ioctl(cdi, cmd, NULL);
|
|
||||||
}
|
|
||||||
} /* switch */
|
|
||||||
|
|
||||||
/* do the device specific ioctls */
|
/*
|
||||||
|
* Finally, do the device specific ioctls
|
||||||
|
*/
|
||||||
if (CDROM_CAN(CDC_IOCTLS))
|
if (CDROM_CAN(CDC_IOCTLS))
|
||||||
return cdo->dev_ioctl(cdi, cmd, arg);
|
return cdi->ops->dev_ioctl(cdi, cmd, arg);
|
||||||
|
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue