patch-2.1.34 linux/drivers/cdrom/cdrom.c

Next file: linux/drivers/char/lp.c
Previous file: linux/drivers/cdrom/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.33/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
@@ -1,9 +1,9 @@
 /* cdrom.c. Common ioctl and open routines for various Linux cdrom drivers. -*- linux-c -*-
-   Copyright (c) 1996 David van Leeuwen. 
+   Copyright (c) 1996, 1997 David A. van Leeuwen.
 
    The routines in the file should provide an interface between
    software accessing cdroms and the various drivers that implement
-   specific hardware devices. 
+   specific hardware devices.
 
  */
 
@@ -21,9 +21,29 @@
 #include <linux/cdrom.h>
 #include <linux/ucdrom.h>
 
+/* define CHECKTYPE if you want to check for audio/data cdrom. You
+   must use cd player programs that respect the O_NONBLOCK option for
+   opening the cdrom-device for ioctl-commanding only. (See
+   Documentation/cdrom/cdrom-standard.tex). Patches for a number of cd
+   players (CDplayer-2.0, cd-console-1.1, workbone-2.3, cdtool-1.0,
+   cdp-0.33, cdplayer-0.4, cdthing-1.4, lyn0.8a, playcd-1.0) can be
+   found in directory
+
+	ftp://ftp.gwdg.de/pub/linux/cdrom/drivers/cm206/
+
+   A runtime configuration program "checktype.c" to allow for cdrom
+   medium type checking can be found at the same location.
+
+   In order to be able to get neat system errors like "No medium
+   found" or "Wrong medium type" upon attempting to mount/play an
+   empty slot, mount an audio disc or play a data disc, you need to
+   have a new libc.5.so.  */
+#undef CHECKTYPE
+
 #define FM_WRITE	0x2                 /* file mode write bit */
 
-#define VERSION "Generic CD-ROM driver, v 1.21 1996/11/08 03:24:49"
+#define VERSION "$Id: cdrom.c,v 1.7 1997/02/27 22:14:26 david Exp $"
+#define REVISION "$Revision: 1.7 $"
 
 /* Not-exported routines. */
 static int cdrom_open(struct inode *ip, struct file *fp);
@@ -34,19 +54,19 @@
 
 struct file_operations cdrom_fops =
 {
-        NULL,                           /* lseek */
-        block_read,                     /* read - general block-dev read */
-        block_write,                    /* write - general block-dev write */
-        NULL,                           /* readdir */
-        NULL,                           /* poll */
-        cdrom_ioctl,                    /* ioctl */
-        NULL,                           /* mmap */
-        cdrom_open,                     /* open */
-        cdrom_release,                  /* release */
-        NULL,                           /* fsync */
-        NULL,                           /* fasync */
-        cdrom_media_changed,            /* media_change */
-        NULL                            /* revalidate */
+	NULL,                           /* lseek */
+	block_read,                     /* read - general block-dev read */
+	block_write,                    /* write - general block-dev write */
+	NULL,                           /* readdir */
+	NULL,                           /* poll */
+	cdrom_ioctl,                    /* ioctl */
+	NULL,                           /* mmap */
+	cdrom_open,                     /* open */
+	cdrom_release,                  /* release */
+	NULL,                           /* fsync */
+	NULL,                           /* fasync */
+	cdrom_media_changed,            /* media_change */
+	NULL                            /* revalidate */
 };
 
 static struct cdrom_device_info *cdromdevs[MAX_BLKDEV] = {
@@ -62,68 +82,71 @@
 #define ENSURE(call, bits) if (cdo->call == NULL) *change_capability &= ~(bits)
 
 /* We don't use $name$ yet, but it could be used for the /proc
- * filesystem in the future, or for other purposes.  
+ * filesystem in the future, or for other purposes.
  */
 int register_cdrom(struct cdrom_device_info *cdi, char *name)
 {
-        int major = MAJOR (cdi->dev);
-        struct cdrom_device_ops *cdo = cdi->ops;
-        int *change_capability = (int *)&cdo->capability; /* hack */
-
-        if (major < 0 || major >= MAX_BLKDEV)
-                return -1;
-        if (cdo->open == NULL || cdo->release == NULL)
-                return -2;
-        ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
-        ENSURE(lock_door, CDC_LOCK);
-        ENSURE(select_speed, CDC_SELECT_SPEED);
-        ENSURE(select_disc, CDC_SELECT_DISC);
-        ENSURE(get_last_session, CDC_MULTI_SESSION);
-        ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
-        ENSURE(media_changed, CDC_MEDIA_CHANGED);
-        if (cdromdevs[major]==NULL) cdo->n_minors = 0;
-        else cdo->n_minors++; 
-        cdi->next = cdromdevs[major];
-        cdromdevs[major] = cdi;
-        cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK;
-        cdi->mc_flags = 0;
-        return 0;
+	int major = MAJOR (cdi->dev);
+	struct cdrom_device_ops *cdo = cdi->ops;
+	int *change_capability = (int *)&cdo->capability; /* hack */
+
+	if (major < 0 || major >= MAX_BLKDEV)
+		return -1;
+	if (cdo->open == NULL || cdo->release == NULL)
+		return -2;
+	ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
+	ENSURE(lock_door, CDC_LOCK);
+	ENSURE(select_speed, CDC_SELECT_SPEED);
+	ENSURE(select_disc, CDC_SELECT_DISC);
+	ENSURE(get_last_session, CDC_MULTI_SESSION);
+	ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
+	ENSURE(media_changed, CDC_MEDIA_CHANGED);
+	if (cdromdevs[major]==NULL) cdo->n_minors = 0;
+	else cdo->n_minors++;
+	cdi->next = cdromdevs[major];
+	cdromdevs[major] = cdi;
+	cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK;
+#ifdef CHECKTYPE
+	cdi->options |= CDO_CHECK_TYPE;
+#endif
+	cdi->mc_flags = 0;
+	return 0;
 }
 #undef ENSURE
 
 int unregister_cdrom(struct cdrom_device_info *unreg)
 {
-        struct cdrom_device_info *cdi, *prev;
-        int major = MAJOR (unreg->dev);
+	struct cdrom_device_info *cdi, *prev;
+	int major = MAJOR (unreg->dev);
 
-        if (major < 0 || major >= MAX_BLKDEV)
-                return -1;
+	if (major < 0 || major >= MAX_BLKDEV)
+		return -1;
 
-        prev = NULL;
-        cdi = cdromdevs[major];
-        while (cdi != NULL && cdi->dev != unreg->dev) {
-                prev = cdi;
-                cdi = cdi->next;
-        }
-
-        if (cdi == NULL)
-                return -2;
-        if (prev)
-                prev->next = cdi->next;
-        else
-                cdromdevs[major] = cdi->next;
-        cdi->ops->n_minors--;
-        return 0;
+	prev = NULL;
+	cdi = cdromdevs[major];
+	while (cdi != NULL && cdi->dev != unreg->dev) {
+		prev = cdi;
+		cdi = cdi->next;
+	}
+
+	if (cdi == NULL)
+		return -2;
+	if (prev)
+		prev->next = cdi->next;
+	else
+		cdromdevs[major] = cdi->next;
+	cdi->ops->n_minors--;
+	return 0;
 }
 
 static
 struct cdrom_device_info *cdrom_find_device (kdev_t dev)
 {
-        struct cdrom_device_info *cdi = cdromdevs[MAJOR (dev)];
+	struct cdrom_device_info *cdi = cdromdevs[MAJOR (dev)];
 
-        while (cdi != NULL && cdi->dev != dev)
-                cdi = cdi->next;
-        return cdi;
+	while (cdi != NULL && cdi->dev != dev)
+		cdi = cdi->next;
+	return cdi;
 }
 
 /* We use the open-option O_NONBLOCK to indicate that the
@@ -141,83 +164,88 @@
 int cdrom_open(struct inode *ip, struct file *fp)
 {
 	kdev_t dev = ip->i_rdev;
-        struct cdrom_device_info *cdi = cdrom_find_device(dev);
-        int purpose = !!(fp->f_flags & O_NONBLOCK);
-        int ret=0;
-
-        if (cdi == NULL)
-                return -ENODEV;
-        if (fp->f_mode & FM_WRITE)
-                return -EROFS;				
-        purpose = purpose || !(cdi->options & CDO_USE_FFLAGS);
-        if (cdi->use_count || purpose) 
-                ret = cdi->ops->open(cdi, purpose); 
-        else 
-                ret = open_for_data(cdi);
-        if (!ret) cdi->use_count++;
-        return ret;
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
+	int purpose = !!(fp->f_flags & O_NONBLOCK);
+	int ret=0;
+
+	if (cdi == NULL)
+		return -ENODEV;
+	if (fp->f_mode & FM_WRITE)
+		return -EROFS;
+	purpose = purpose || !(cdi->options & CDO_USE_FFLAGS);
+	if (cdi->use_count || purpose)
+		ret = cdi->ops->open(cdi, purpose);
+	else
+		ret = open_for_data(cdi);
+	if (!ret) cdi->use_count++;
+	return ret;
 }
 
 static
 int open_for_data(struct cdrom_device_info * cdi)
 {
-        int ret;
-        struct cdrom_device_ops *cdo = cdi->ops;
-        if (cdo->drive_status != NULL) {
-                int ds = cdo->drive_status(cdi, CDSL_CURRENT);
-                if (ds == CDS_TRAY_OPEN) {
-                        /* can/may i close it? */
-                        if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
-                            cdi->options & CDO_AUTO_CLOSE) {
-                                if (cdo->tray_move(cdi,0))
-                                        return -EIO;
-                        } else
-                                return -ENXIO; /* can't close: too bad */
-                        ds = cdo->drive_status(cdi, CDSL_CURRENT);
-                        if (ds == CDS_NO_DISC)
-                                return -ENXIO;
-                }
-        }
-        if (cdo->disc_status != NULL) {
-                int ds = cdo->disc_status(cdi);
-                if (ds == CDS_NO_DISC)
-                        return -ENXIO;
-                if (cdi->options & CDO_CHECK_TYPE &&
-                    ds != CDS_DATA_1)
-                        return -ENODATA;
-        }
-        /* all is well, we can open the device */
-        ret = cdo->open(cdi, 0); /* open for data */
-        if (cdo->capability & ~cdi->mask & CDC_LOCK &&
-            cdi->options & CDO_LOCK)
-                cdo->lock_door(cdi, 1);
-        return ret;
+	int ret;
+	struct cdrom_device_ops *cdo = cdi->ops;
+	if (cdo->drive_status != NULL) {
+		int ds = cdo->drive_status(cdi, CDSL_CURRENT);
+		if (ds == CDS_TRAY_OPEN) {
+			/* can/may i close it? */
+			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
+			    cdi->options & CDO_AUTO_CLOSE) {
+				if (cdo->tray_move(cdi,0))
+					return -EIO;
+			} else
+				return -ENOMEDIUM; /* can't close: too bad */
+			ds = cdo->drive_status(cdi, CDSL_CURRENT);
+			if (ds == CDS_NO_DISC)
+				return -ENOMEDIUM;
+		}
+	}
+	if (cdo->disc_status != NULL) {
+		int ds = cdo->disc_status(cdi);
+		if (ds == CDS_NO_DISC)
+			return -ENOMEDIUM;
+		if (cdi->options & CDO_CHECK_TYPE &&
+		    ds != CDS_DATA_1)
+			return -EMEDIUMTYPE;
+	}
+	/* all is well, we can open the device */
+	ret = cdo->open(cdi, 0); /* open for data */
+	if (cdo->capability & ~cdi->mask & CDC_LOCK &&
+	    cdi->options & CDO_LOCK)
+		cdo->lock_door(cdi, 1);
+	return ret;
 }
 
 /* Admittedly, the logic below could be performed in a nicer way. */
 static
 int cdrom_release(struct inode *ip, struct file *fp)
 {
-        kdev_t dev = ip->i_rdev;
-        struct cdrom_device_info *cdi = cdrom_find_device (dev);
-        struct cdrom_device_ops *cdo;
-	
-        if (cdi == NULL)
-                return 0;
-        cdo = cdi->ops;        
-        if (cdi->use_count == 1 &&      /* last process that closes dev*/
-            cdi->options & CDO_LOCK &&  
-            cdo->capability & ~cdi->mask & CDC_LOCK) 
-                cdo->lock_door(cdi, 0);
-        cdo->release(cdi);
-        if (cdi->use_count > 0) cdi->use_count--;
-        if (cdi->use_count == 0) {      /* last process that closes dev*/
-                sync_dev(dev);
-                invalidate_buffers(dev);
-                if (cdi->options & CDO_AUTO_EJECT &&
-                    cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)
-                        cdo->tray_move(cdi, 1);
-        }
+	kdev_t dev = ip->i_rdev;
+	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_ops *cdo;
+	int opened_for_data;
+
+	if (cdi == NULL)
+		return 0;
+	opened_for_data = !(cdi->options & CDO_USE_FFLAGS) ||
+		!(fp && fp->f_flags & O_NONBLOCK);
+	cdo = cdi->ops;
+	if (cdi->use_count == 1 &&      /* last process that closes dev*/
+	    opened_for_data &&
+	    cdi->options & CDO_LOCK &&
+	    cdo->capability & ~cdi->mask & CDC_LOCK)
+		cdo->lock_door(cdi, 0);
+	cdo->release(cdi);
+	if (cdi->use_count > 0) cdi->use_count--;
+	if (cdi->use_count == 0) {      /* last process that closes dev*/
+		sync_dev(dev);
+		invalidate_buffers(dev);
+		if (opened_for_data &&
+		    cdi->options & CDO_AUTO_EJECT &&
+		    cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)
+			cdo->tray_move(cdi, 1);
+	}
 	return 0;
 }
 
@@ -230,37 +258,37 @@
 static
 int media_changed(struct cdrom_device_info *cdi, int queue)
 {
-        unsigned int mask = (1 << (queue & 1));
-        int ret = !!(cdi->mc_flags & mask);
+	unsigned int mask = (1 << (queue & 1));
+	int ret = !!(cdi->mc_flags & mask);
 
-        /* changed since last call? */
-        if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
-                cdi->mc_flags = 0x3;    /* set bit on both queues */
-                ret |= 1;
-        }
-        cdi->mc_flags &= ~mask;         /* clear bit */
-        return ret;
+	/* changed since last call? */
+	if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
+		cdi->mc_flags = 0x3;    /* set bit on both queues */
+		ret |= 1;
+	}
+	cdi->mc_flags &= ~mask;         /* clear bit */
+	return ret;
 }
 
 static
 int cdrom_media_changed(kdev_t dev)
 {
-        struct cdrom_device_info *cdi = cdrom_find_device (dev);
-        if (cdi == NULL)
-                return -ENODEV;
-        if (cdi->ops->media_changed == NULL)
-                return -EINVAL;
-        return media_changed(cdi, 0);
+	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	if (cdi == NULL)
+		return -ENODEV;
+	if (cdi->ops->media_changed == NULL)
+		return -EINVAL;
+	return media_changed(cdi, 0);
 }
 
 /* Requests to the low-level drivers will /always/ be done in the
-   following format convention: 
+   following format convention:
 
    CDROM_LBA: all data-related requests.
-   CDROM_MSF: all audio-related requests. 
+   CDROM_MSF: all audio-related requests.
 
    However, a low-level implementation is allowed to refuse this
-   request, and return information in its own favorite format.  
+   request, and return information in its own favorite format.
 
    It doesn't make sense /at all/ to ask for a play_audio in LBA
    format, or ask for multi-session info in MSF format. However, for
@@ -271,22 +299,22 @@
 
 static
 void sanitize_format(union cdrom_addr *addr,
-                     u_char * curr, u_char requested)
+		     u_char * curr, u_char requested)
 {
-        if (*curr == requested)
-                return;                 /* nothing to be done! */
-        if (requested == CDROM_LBA) {
-                addr->lba = (int) addr->msf.frame +
-                        75 * (addr->msf.second - 2 + 60 * addr->msf.minute);
-        } else {                        /* CDROM_MSF */
-                int lba = addr->lba;
-                addr->msf.frame = lba % 75;
-                lba /= 75;
-                lba += 2;
-                addr->msf.second = lba % 60;
-                addr->msf.minute = lba / 60;
-        }
-        *curr = requested;
+	if (*curr == requested)
+		return;                 /* nothing to be done! */
+	if (requested == CDROM_LBA) {
+		addr->lba = (int) addr->msf.frame +
+			75 * (addr->msf.second - 2 + 60 * addr->msf.minute);
+	} else {                        /* CDROM_MSF */
+		int lba = addr->lba;
+		addr->msf.frame = lba % 75;
+		lba /= 75;
+		lba += 2;
+		addr->msf.second = lba % 60;
+		addr->msf.minute = lba / 60;
+	}
+	*curr = requested;
 }
 
 /* All checking and format change makes this code really hard to read!
@@ -296,7 +324,7 @@
  * shouldn't be in inner loops.
  */
 #define GETARG(type, x) { \
-        int ret=verify_area(VERIFY_READ, (void *) arg, sizeof x); \
+	int ret=verify_area(VERIFY_READ, (void *) arg, sizeof x); \
 	    if (ret) return ret; \
 	    copy_from_user(&x, (type *) arg, sizeof x); }
 #define PUTARG(type, x) { \
@@ -318,238 +346,286 @@
  * memory-verification is performed for these ioctls.
  */
 static
+int check_for_audio_disc(struct cdrom_device_info * cdi,
+			 struct cdrom_device_ops * cdo);
+
+static
 int cdrom_ioctl(struct inode *ip, struct file *fp,
-                unsigned int cmd, unsigned long arg)
+		unsigned int cmd, unsigned long arg)
 {
-        kdev_t dev = ip->i_rdev;
-        struct cdrom_device_info *cdi = cdrom_find_device (dev);
-        struct cdrom_device_ops *cdo;
-
-        if (cdi == NULL)
-                return -ENODEV;
-        cdo = cdi->ops;
-        /* the first few commands do not deal with audio capabilities, but
-           only with routines in cdrom device operations. */
-        switch (cmd) {
-                /* maybe we should order cases after statistics of use? */
-
-        case CDROMMULTISESSION: 
-        {
-                struct cdrom_multisession ms_info;
-                u_char requested_format;
-                if (!(cdo->capability & CDC_MULTI_SESSION))
-                        return -EINVAL;
-                GETARG(struct cdrom_multisession, ms_info);
-                requested_format = ms_info.addr_format;
-                ms_info.addr_format = CDROM_LBA;
-                cdo->get_last_session(cdi, &ms_info);
-                sanitize_format(&ms_info.addr, &ms_info.addr_format, 
-                                requested_format);
-                PUTARG(struct cdrom_multisession, ms_info);
-                return 0;
-        }
-                
-        case CDROMEJECT:
-                if (cdo->capability & ~cdi->mask & CDC_OPEN_TRAY) {
+	kdev_t dev = ip->i_rdev;
+	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_ops *cdo;
+
+	if (cdi == NULL)
+		return -ENODEV;
+	cdo = cdi->ops;
+	/* the first few commands do not deal with audio capabilities, but
+	   only with routines in cdrom device operations. */
+	switch (cmd) {
+		/* maybe we should order cases after statistics of use? */
+
+	case CDROMMULTISESSION:
+	{
+		struct cdrom_multisession ms_info;
+		u_char requested_format;
+		if (!(cdo->capability & CDC_MULTI_SESSION))
+			return -EINVAL;
+		GETARG(struct cdrom_multisession, ms_info);
+		requested_format = ms_info.addr_format;
+		ms_info.addr_format = CDROM_LBA;
+		cdo->get_last_session(cdi, &ms_info);
+		sanitize_format(&ms_info.addr, &ms_info.addr_format,
+				requested_format);
+		PUTARG(struct cdrom_multisession, ms_info);
+		return 0;
+	}
+
+	case CDROMEJECT:
+		if (cdo->capability & ~cdi->mask & CDC_OPEN_TRAY) {
 			if (cdi->use_count == 1) {
-			        if (cdo->capability & ~cdi->mask & CDC_LOCK)
+				if (cdo->capability & ~cdi->mask & CDC_LOCK)
 					cdo->lock_door(cdi, 0);
 				return cdo->tray_move(cdi, 1);
 			} else
 				return -EBUSY;
 		} else
-                        return -EINVAL;
-		
-        case CDROMCLOSETRAY:
-                if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY)
+			return -EINVAL;
+
+	case CDROMCLOSETRAY:
+		if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY)
 			return cdo->tray_move(cdi, 0);
-                else
-                        return -EINVAL;
-                
-        case CDROMEJECT_SW:
-                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 (cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED) {
-                	if (arg == CDSL_CURRENT)
-                        	return media_changed(cdi, 1);
-                	else if ((int)arg < cdi->capacity &&
-                                 cdo->capability & ~cdi->mask
-                                 &CDC_SELECT_DISC)
-                        	return cdo->media_changed (cdi, arg);
-                        else
-                        	return -EINVAL;
-                }
-                else
-                        return -EINVAL;
-                
-        case CDROM_SET_OPTIONS:
-                cdi->options |= (int) arg;
-                return cdi->options;
-		
-        case CDROM_CLEAR_OPTIONS:
-                cdi->options &= ~(int) arg;
-                return cdi->options;
-                
-        case CDROM_SELECT_SPEED:
-                if ((int)arg <= cdi->speed &&
-                    cdo->capability & ~cdi->mask & CDC_SELECT_SPEED)
-                        return cdo->select_speed(cdi, arg);
-                else
-                        return -EINVAL;
-
-        case CDROM_SELECT_DISC:
-                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
-                	return cdo->select_disc(cdi, arg);
-                if ((int)arg < cdi->capacity &&
-                    cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
+		else
+			return -EINVAL;
+
+	case CDROMEJECT_SW:
+		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 (cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED) {
+			if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
+			    || arg == CDSL_CURRENT)
+				/* cannot select disc or select current disc */
+				return media_changed(cdi, 1);
+			if ((unsigned int)arg < cdi->capacity)
+				return cdo->media_changed (cdi, arg);
+			return -EINVAL;
+		} else
+			return -EINVAL;
+
+	case CDROM_SET_OPTIONS:
+		cdi->options |= (int) arg;
+		return cdi->options;
+
+	case CDROM_CLEAR_OPTIONS:
+		cdi->options &= ~(int) arg;
+		return cdi->options;
+
+	case CDROM_SELECT_SPEED:
+		if ((int)arg <= cdi->speed &&
+		    cdo->capability & ~cdi->mask & CDC_SELECT_SPEED)
+			return cdo->select_speed(cdi, arg);
+		else
+			return -EINVAL;
+
+	case CDROM_SELECT_DISC:
+		if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
 			return cdo->select_disc(cdi, arg);
-                else
+		if ((int)arg < cdi->capacity &&
+		    cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
+			return cdo->select_disc(cdi, arg);
+		else
 			return -EINVAL;
-		
+
 /* 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: {
-                struct cdrom_mcn mcn;
-                if (!(cdo->capability & CDC_MCN))
-                        return -EINVAL;
-                if (!cdo->get_mcn(cdi, &mcn)) {
-                        PUTARG(struct cdrom_mcn, mcn);
-                        return 0;
-                }
-                return -EINVAL;
-        }
-                
-        case CDROM_DRIVE_STATUS:
-                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
-                	return cdo->drive_status(cdi, arg);
-                if (cdo->drive_status == NULL ||
-                            ! (cdo->capability & ~cdi->mask & CDC_SELECT_DISC
-                            && (int)arg < cdi->capacity)) 
-                	return -EINVAL;
-                else
+	case CDROM_GET_MCN: {
+		struct cdrom_mcn mcn;
+		if (!(cdo->capability & CDC_MCN))
+			return -EINVAL;
+		if (!cdo->get_mcn(cdi, &mcn)) {
+			PUTARG(struct cdrom_mcn, mcn);
+			return 0;
+		}
+		return -EINVAL;
+	}
+
+	case CDROM_DRIVE_STATUS:
+		if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
+			return cdo->drive_status(cdi, arg);
+		if (cdo->drive_status == NULL ||
+			    ((cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
+			    && (int)arg >= cdi->capacity))
+			return -EINVAL;
+		else
 			return cdo->drive_status(cdi, arg);
-		
-        case CDROM_DISC_STATUS:
+
+	case CDROM_DISC_STATUS:
 		if (cdo->disc_status == NULL)
 			return -EINVAL;
 		else
 			return cdo->disc_status(cdi);
 
-        case CDROM_CHANGER_NSLOTS:
-        	return cdi->capacity;
+	case CDROM_CHANGER_NSLOTS:
+		return cdi->capacity;
 
 /* The following is not implemented, because there are too many
  * different data type. We could support /1/ raw mode, that is large
  * enough to hold everything.
  */
-		
+
 #if 0
-        case CDROMREADMODE1: {
-                struct cdrom_msf msf;
-                char buf[CD_FRAMESIZE];
-                GETARG(struct cdrom_msf, msf);
-                if (!cdo->read_audio(dev, cmd, &msf, &buf, cdi)) {
-                        PUTARG(char *, buf);
-                        return 0;
-                }
-                return -EINVAL;
-        }
+	case CDROMREADMODE1: {
+		struct cdrom_msf msf;
+		char buf[CD_FRAMESIZE];
+		GETARG(struct cdrom_msf, msf);
+		if (!cdo->read_audio(dev, cmd, &msf, &buf, cdi)) {
+			PUTARG(char *, buf);
+			return 0;
+		}
+		return -EINVAL;
+	}
 #endif
 	} /* switch */
-	
+
 /* Now all the audio-ioctls follow, they are all routed through the
    same call audio_ioctl(). */
 
-	if (cdo->capability & CDC_PLAY_AUDIO)
+#define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret
+
+	if (cdo->capability & CDC_PLAY_AUDIO) {
+		int ret;
 		switch (cmd) {
-                case CDROMSUBCHNL: 
-                {
-                        struct cdrom_subchnl q;
-                        u_char requested, back;
-                        GETARG(struct cdrom_subchnl, q);
-                        requested = q.cdsc_format;
-                        q.cdsc_format = CDROM_MSF;
-                        if (!cdo->audio_ioctl(cdi, cmd, &q)) {
-                                back = q.cdsc_format; /* local copy */
-                                sanitize_format(&q.cdsc_absaddr, &back,
-                                requested);
-                                sanitize_format(&q.cdsc_reladdr,
-                                &q.cdsc_format, requested);
-                                PUTARG(struct cdrom_subchnl, q);
-                                return 0;
-                        } else
-                                return -EINVAL;
-                }
-                case CDROMREADTOCHDR: {
-                        struct cdrom_tochdr header;
-                        GETARG(struct cdrom_tochdr, header);
-                        if (!cdo->audio_ioctl(cdi, cmd, &header)) {
-                                PUTARG(struct cdrom_tochdr, header);
-                                return 0;
-                        } else
-                                return -EINVAL;
-                }
-                case CDROMREADTOCENTRY: {
-                        struct cdrom_tocentry entry;
-                        u_char requested_format;
-                        GETARG(struct cdrom_tocentry, entry);
-                        requested_format = entry.cdte_format;
-                        /* make interface to low-level uniform */
-                        entry.cdte_format = CDROM_MSF; 
-                        if (!(cdo->audio_ioctl(cdi, cmd, &entry))) {
-                                sanitize_format(&entry.cdte_addr,
-                                &entry.cdte_format, requested_format);
-                                PUTARG(struct cdrom_tocentry, entry);
-                                return 0;
-                        } else
-                                return -EINVAL;
-                }
-                case CDROMPLAYMSF: {
-                        struct cdrom_msf msf;
-                        GETARG(struct cdrom_msf, msf);
-                        return cdo->audio_ioctl(cdi, cmd, &msf);
-                }
-                case CDROMPLAYTRKIND: {
-                        struct cdrom_ti track_index;
-                        GETARG(struct cdrom_ti, track_index);
-                        return cdo->audio_ioctl(cdi, cmd, &track_index);
-                }
-                case CDROMVOLCTRL: {
-                        struct cdrom_volctrl volume;
-                        GETARG(struct cdrom_volctrl, volume);
-                        return cdo->audio_ioctl(cdi, cmd, &volume);
-                }
-                case CDROMVOLREAD: {
-                        struct cdrom_volctrl volume;
-                        if (!cdo->audio_ioctl(cdi, cmd, &volume)) {
-                                PUTARG(struct cdrom_volctrl, volume);
-                                return 0;
-                        }
-                        return -EINVAL;
-                }
-                case CDROMSTART:
-                case CDROMSTOP:
-                case CDROMPAUSE:
-                case CDROMRESUME:
+		case CDROMSUBCHNL:
+		{
+			struct cdrom_subchnl q;
+			u_char requested, back;
+			GETARG(struct cdrom_subchnl, q);
+			requested = q.cdsc_format;
+			q.cdsc_format = CDROM_MSF;
+			if (!cdo->audio_ioctl(cdi, cmd, &q)) {
+				back = q.cdsc_format; /* local copy */
+				sanitize_format(&q.cdsc_absaddr, &back,
+				requested);
+				sanitize_format(&q.cdsc_reladdr,
+				&q.cdsc_format, requested);
+				PUTARG(struct cdrom_subchnl, q);
+				return 0;
+			} else
+				return -EINVAL;
+		}
+		case CDROMREADTOCHDR: {
+			struct cdrom_tochdr header;
+			GETARG(struct cdrom_tochdr, header);
+			CHECKAUDIO;
+			if (!cdo->audio_ioctl(cdi, cmd, &header)) {
+				PUTARG(struct cdrom_tochdr, header);
+				return 0;
+			} else
+				return -EINVAL;
+		}
+		case CDROMREADTOCENTRY: {
+			struct cdrom_tocentry entry;
+			u_char requested_format;
+			GETARG(struct cdrom_tocentry, entry);
+			requested_format = entry.cdte_format;
+			/* make interface to low-level uniform */
+			entry.cdte_format = CDROM_MSF;
+			if (!(cdo->audio_ioctl(cdi, cmd, &entry))) {
+				sanitize_format(&entry.cdte_addr,
+				&entry.cdte_format, requested_format);
+				PUTARG(struct cdrom_tocentry, entry);
+				return 0;
+			} else
+				return -EINVAL;
+		}
+		case CDROMPLAYMSF: {
+			struct cdrom_msf msf;
+			GETARG(struct cdrom_msf, msf);
+			CHECKAUDIO;
+			return cdo->audio_ioctl(cdi, cmd, &msf);
+		}
+		case CDROMPLAYTRKIND: {
+			struct cdrom_ti track_index;
+			GETARG(struct cdrom_ti, track_index);
+			CHECKAUDIO;
+			return cdo->audio_ioctl(cdi, cmd, &track_index);
+		}
+		case CDROMVOLCTRL: {
+			struct cdrom_volctrl volume;
+			GETARG(struct cdrom_volctrl, volume);
+			return cdo->audio_ioctl(cdi, cmd, &volume);
+		}
+		case CDROMVOLREAD: {
+			struct cdrom_volctrl volume;
+			if (!cdo->audio_ioctl(cdi, cmd, &volume)) {
+				PUTARG(struct cdrom_volctrl, volume);
+				return 0;
+			}
+			return -EINVAL;
+		}
+		case CDROMSTART:
+			CHECKAUDIO;
+		case CDROMSTOP:
+		case CDROMPAUSE:
+		case CDROMRESUME:
 			return cdo->audio_ioctl(cdi, cmd, NULL);
 		} /* switch */
+	}
 
 	if (cdo->dev_ioctl != NULL)     /* device specific ioctls? */
 		return cdo->dev_ioctl(cdi, cmd, arg);
 	return -EINVAL;
 }
 
+/* This code is similar to that in open_for_data. The routine is called
+   in case a audio play operation is requested. It doesn't make much sense
+   do do this on a data disc.
+ */
+int check_for_audio_disc(struct cdrom_device_info * cdi,
+			 struct cdrom_device_ops * cdo)
+{
+	if (!(cdi->options & CDO_CHECK_TYPE))
+		return 0;
+	if (cdo->drive_status != NULL) {
+		int ds = cdo->drive_status(cdi, CDSL_CURRENT);
+		if (ds == CDS_TRAY_OPEN) {
+			/* can/may i close it? */
+			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
+			    cdi->options & CDO_AUTO_CLOSE) {
+				if (cdo->tray_move(cdi,0))
+					return -EIO;
+			} else
+				return -ENOMEDIUM; /* can't close: too bad */
+			ds = cdo->drive_status(cdi, CDSL_CURRENT);
+			if (ds == CDS_NO_DISC)
+				return -ENOMEDIUM;
+		}
+		if (cdo->disc_status != NULL) {
+			ds = cdo->disc_status(cdi);
+			if (ds == CDS_NO_DISC)
+				return -ENOMEDIUM;
+			if (ds != CDS_AUDIO)
+				return -EMEDIUMTYPE;
+		}
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(register_cdrom);
+EXPORT_SYMBOL(unregister_cdrom);
+EXPORT_SYMBOL(cdrom_fops);
+
 #ifdef MODULE
 int init_module(void)
 {
-	printk(KERN_INFO "Module inserted: " VERSION "\n");
+	printk(KERN_INFO "Module cdrom: Generic CDROM driver " REVISION "\n");
 	return 0;
 }
 
@@ -564,6 +640,6 @@
 /*
  * Local variables:
  * comment-column: 40
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I. -I/usr/src/linux-obj/include -Wall -Wstrict-prototypes -O2 -m486 -c cdrom.c -o cdrom.o"
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o cdrom.o cdrom.c"
  * End:
  */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov