/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */
/* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
ENSURE(get_mcn, CDC_MCN);
ENSURE(reset, CDC_RESET);
ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
+ ENSURE(dev_ioctl, CDC_IOCTLS);
ENSURE(generic_packet, CDC_GENERIC_PACKET);
cdi->mc_flags = 0;
cdo->n_minors = 0;
if (fp->f_mode & FMODE_WRITE) {
ret = -EROFS;
if (cdrom_open_write(cdi))
- goto err_release;
+ goto err;
if (!CDROM_CAN(CDC_RAM))
- goto err_release;
+ goto err;
ret = 0;
cdi->media_written = 0;
}
not be mounting, but opening with O_NONBLOCK */
check_disk_change(ip->i_bdev);
return 0;
-err_release:
- cdi->ops->release(cdi);
err:
cdi->use_count--;
return ret;
return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);
}
-static int cdrom_ioctl_multisession(struct cdrom_device_info *cdi,
- void __user *argp)
+/* 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)
{
- struct cdrom_multisession ms_info;
- u8 requested_format;
+ struct cdrom_device_ops *cdo = cdi->ops;
int ret;
- cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n");
-
- 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)
+ /* 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;
- 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;
-}
-
-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)) {
- int ret = cdi->ops->lock_door(cdi, 0);
- if (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))
+ return -ENOSYS;
+ IOCTL_IN(arg, struct cdrom_multisession, ms_info);
+ requested_format = ms_info.addr_format;
+ 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)))
return ret;
- }
-
- return cdi->ops->tray_move(cdi, 1);
-}
-
-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 cdi->ops->tray_move(cdi, 0);
-}
-
-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;
-}
-
-static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- struct cdrom_changer_info *info;
- int ret;
-
- cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");
-
- if (!CDROM_CAN(CDC_MEDIA_CHANGED))
- return -ENOSYS;
-
- /* cannot select disc or select current disc */
- if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
- return media_changed(cdi, 1);
-
- if ((unsigned int)arg >= cdi->capacity)
- return -EINVAL;
-
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
+ 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;
+ }
- ret = cdrom_read_mech_status(cdi, info);
- if (!ret)
- ret = info->slots[arg].change;
- kfree(info);
- return ret;
-}
+ case CDROMEJECT: {
+ 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;
-static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n");
+ return cdo->tray_move(cdi, 1);
+ }
- /*
- * 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:
- 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))
+ case CDROMCLOSETRAY: {
+ cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n");
+ if (!CDROM_CAN(CDC_CLOSE_TRAY))
return -ENOSYS;
- }
- cdi->options |= (int) arg;
- return cdi->options;
-}
-
-static int cdrom_ioctl_clear_options(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n");
+ return cdo->tray_move(cdi, 0);
+ }
- cdi->options &= ~(int) arg;
- return cdi->options;
-}
+ case CDROMEJECT_SW: {
+ 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;
+ }
-static int cdrom_ioctl_select_speed(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n");
+ case CDROM_MEDIA_CHANGED: {
+ struct cdrom_changer_info *info;
+ int changed;
- if (!CDROM_CAN(CDC_SELECT_SPEED))
- return -ENOSYS;
- return cdi->ops->select_speed(cdi, arg);
-}
+ cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n");
+ if (!CDROM_CAN(CDC_MEDIA_CHANGED))
+ return -ENOSYS;
-static int cdrom_ioctl_select_disc(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n");
+ /* cannot select disc or select current disc */
+ if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
+ return media_changed(cdi, 1);
- if (!CDROM_CAN(CDC_SELECT_DISC))
- return -ENOSYS;
-
- if (arg != CDSL_CURRENT && arg != CDSL_NONE) {
- if ((int)arg >= cdi->capacity)
+ if ((unsigned 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);
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
- cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n");
- return cdrom_select_disc(cdi, arg);
-}
+ if ((ret = cdrom_read_mech_status(cdi, info))) {
+ kfree(info);
+ return ret;
+ }
-static int cdrom_ioctl_reset(struct cdrom_device_info *cdi,
- struct block_device *bdev)
-{
- cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
+ changed = info->slots[arg].change;
+ kfree(info);
+ return changed;
+ }
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- if (!CDROM_CAN(CDC_RESET))
- return -ENOSYS;
- invalidate_bdev(bdev, 0);
- return cdi->ops->reset(cdi);
-}
+ case CDROM_SET_OPTIONS: {
+ 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... */
+ 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;
+ }
-static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl");
+ case CDROM_CLEAR_OPTIONS: {
+ cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n");
+ cdi->options &= ~(int) arg;
+ return cdi->options;
+ }
- if (!CDROM_CAN(CDC_LOCK))
- return -EDRIVE_CANT_DO_THIS;
+ case CDROM_SELECT_SPEED: {
+ cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n");
+ if (!CDROM_CAN(CDC_SELECT_SPEED))
+ return -ENOSYS;
+ return cdo->select_speed(cdi, arg);
+ }
- keeplocked = arg ? 1 : 0;
+ case CDROM_SELECT_DISC: {
+ cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n");
+ if (!CDROM_CAN(CDC_SELECT_DISC))
+ return -ENOSYS;
- /*
- * 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);
-}
+ 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...
+ */
+ 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);
+ }
-static int cdrom_ioctl_debug(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");
+ 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);
+ }
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- debug = arg ? 1 : 0;
- return debug;
-}
+ case CDROM_LOCKDOOR: {
+ 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))
+ return -EBUSY;
+ return cdo->lock_door(cdi, arg);
+ }
-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);
-}
+ case CDROM_DEBUG: {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");
+ debug = arg ? 1 : 0;
+ return debug;
+ }
-/*
- * The following function is implemented, although very few audio
+ case CDROM_GET_CAPABILITY: {
+ cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
+ return (cdo->capability & ~cdi->mask);
+ }
+
+/* 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!
*/
-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 (!(cdi->ops->capability & CDC_MCN))
- return -ENOSYS;
- ret = cdi->ops->get_mcn(cdi, &mcn);
- if (ret)
- return ret;
-
- if (copy_to_user(argp, &mcn, sizeof(mcn)))
- return -EFAULT;
- cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN successful\n");
- return 0;
-}
-
-static int cdrom_ioctl_drive_status(struct cdrom_device_info *cdi,
- unsigned long arg)
-{
- cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n");
-
- if (!(cdi->ops->capability & CDC_DRIVE_STATUS))
- 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);
-}
-
-/*
- * 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
- */
-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;
- }
-
- 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;
-}
-
-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");
+ case CDROM_GET_MCN: {
+ struct cdrom_mcn mcn;
+ cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n");
+ if (!(cdo->capability & CDC_MCN))
+ return -ENOSYS;
+ 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;
+ }
- 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);
-}
+ case CDROM_DRIVE_STATUS: {
+ 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);
+ }
-/*
- * 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;
+ /* 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: {
+ 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==0 && tracks.cdi==0 && tracks.xa==0)
+ 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;
+ /* Policy mode off */
- /*
- * 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;
+ cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n");
+ return CDS_NO_INFO;
+ }
- 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);
+ case CDROM_CHANGER_NSLOTS: {
+ 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.
- */
+ /* 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)
+ 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.
- */
+ /* 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 CDROMSUBCHNL: {
+ struct cdrom_subchnl q;
+ u_char requested, back;
+ if (!CDROM_CAN(CDC_PLAY_AUDIO))
+ return -ENOSYS;
+ /* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/
+ IOCTL_IN(arg, struct cdrom_subchnl, q);
+ requested = q.cdsc_format;
+ if (!((requested == CDROM_MSF) ||
+ (requested == CDROM_LBA)))
+ return -EINVAL;
+ q.cdsc_format = CDROM_MSF;
+ if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))
+ 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);
+ /* 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 CDROMSTOP:
case CDROMPAUSE:
- case CDROMRESUME:
- return cdrom_ioctl_audioctl(cdi, cmd);
- }
+ 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);
+ }
+ } /* switch */
+ /* do the device specific ioctls */
+ if (CDROM_CAN(CDC_IOCTLS))
+ return cdo->dev_ioctl(cdi, cmd, arg);
+
return -ENOSYS;
}