patch-2.1.9 linux/drivers/block/ide-cd.c

Next file: linux/drivers/block/ide-cd.h
Previous file: linux/drivers/block/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.8/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
@@ -1,5 +1,14 @@
+/* #define VERBOSE_IDE_CD_ERRORS 1 */
 /*
  * linux/drivers/block/ide-cd.c
+ * ATAPI cd-rom driver.  To be used with ide.c.
+ * See Documentation/cdrom/ide-cd for usage information.
+ *
+ * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996  Erik Andersen <andersee@et.byu.edu>
+ *
+ * May be copied or modified under the terms of the GNU General Public License
+ * see linux/COPYING for more information.
  *
  * 1.00  Oct 31, 1994 -- Initial version.
  * 1.01  Nov  2, 1994 -- Fixed problem with starting request in
@@ -108,17 +117,46 @@
  * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
  * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
  *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
- * 3.17a Oct 31, 1996 -- Added module and DMA support.
+ * 3.18  Oct 31, 1996 -- Added module and DMA support.
+ *                       
+ *                       
+ * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
+ *                                 Erik B. Andersen <andersee@et.byu.edu>
+ *                     -- Newer Creative drives don't always set the error
+ *                          register correctly.  Make sure we see media changes
+ *                          regardless.
+ *                     -- Integrate with generic cdrom driver.
+ *                     -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
+ *                          a patch from Ciro Cattuto <>.
+ *                     -- Call set_device_ro.
+ *                     -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ *                          ioctls, based on patch by Erik Andersen
+ *                     -- Add some probes of drive capability during setup.
  *
- * NOTE: Direct audio reads will only work on some types of drive.
- * So far, i've received reports of success for Sony and Toshiba drives.
+ * 4.01  Nov 11, 1996  -- Split into ide-cd.c and ide-cd.h
+ *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE 
+ *                          ioctls in favor of a generalized approach 
+ *                          using the generic cdrom driver.
+ *                     -- Fully integrated with the 2.1.X kernel.
+ *                     -- Other stuff that I forgot (lots of changes)
+ *
+ *
+ * MOSTLY DONE LIST:
+ *  Query the drive to find what features are available
+ *   before trying to use them.
+ *
+ * TO DO LIST:
+ *  Avoid printing error messages for expected errors from the drive.
+ *    (If you are using a cd changer, you may get errors in the kernel
+       logs that are completly expected.  Don't complpain to me about this,
+       unless you have a patch to fix it.  I am working on it...)
+ *  Reset unlocks drive?
+ *  Implement ide_cdrom_disc_status using the generic cdrom interface
+ *  Implement ide_cdrom_select_speed using the generic cdrom interface
+ *  Fix ide_cdrom_reset so that it works (it does nothing right now)
+ *  -- Suggestions are welcome.  Patches that work are more welcome though.
  *
- * ATAPI cd-rom driver.  To be used with ide.c.
- * See Documentation/cdrom/ide-cd for usage information.
  *
- * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
- * May be copied or modified under the terms of the GNU General Public License
- * (../../COPYING).
  */
 
 
@@ -144,389 +182,24 @@
 #include <asm/unaligned.h>
 
 #include "ide.h"
+#include "ide-cd.h"
 
 
-
-/* Turn this on to have the driver print out the meanings of the
-   ATAPI error codes.  This will use up additional kernel-space
-   memory, though. */
-
-#ifndef VERBOSE_IDE_CD_ERRORS
-#define VERBOSE_IDE_CD_ERRORS 0
-#endif
-
-
-/* Turning this on will remove code to work around various nonstandard
-   ATAPI implementations.  If you know your drive follows the standard,
-   this will give you a slightly smaller kernel. */
-
-#ifndef STANDARD_ATAPI
-#define STANDARD_ATAPI 0
-#endif
-
-
-/* Turning this on will disable the door-locking functionality.
-   This is apparently needed for supermount. */
-
-#ifndef NO_DOOR_LOCKING
-#define NO_DOOR_LOCKING 0
-#endif
-
-
-/* Size of buffer to allocate, in blocks, for audio reads. */
-
-#ifndef CDROM_NBLOCKS_BUFFER
-#define CDROM_NBLOCKS_BUFFER 8
-#endif
-
-
-/************************************************************************/
-
-#define SECTOR_SIZE 512
-#define SECTOR_BITS 9
-#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
-
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-/* special command codes for strategy routine. */
-#define PACKET_COMMAND        4315
-#define REQUEST_SENSE_COMMAND 4316
-#define RESET_DRIVE_COMMAND   4317
-
-/* Some ATAPI command opcodes (just like SCSI).
-   (Some other cdrom-specific codes are in cdrom.h.) */
-#define TEST_UNIT_READY         0x00
-#define REQUEST_SENSE           0x03
-#define START_STOP              0x1b
-#define ALLOW_MEDIUM_REMOVAL    0x1e
-#define READ_CAPACITY		0x25
-#define READ_10                 0x28
-#define MODE_SENSE_10           0x5a
-#define MODE_SELECT_10          0x55
-#define READ_CD                 0xbe
-
-#define LOAD_UNLOAD             0xa6
-
-
-/* ATAPI sense keys (mostly copied from scsi.h). */
-
-#define NO_SENSE                0x00
-#define RECOVERED_ERROR         0x01
-#define NOT_READY               0x02
-#define MEDIUM_ERROR            0x03
-#define HARDWARE_ERROR          0x04
-#define ILLEGAL_REQUEST         0x05
-#define UNIT_ATTENTION          0x06
-#define DATA_PROTECT            0x07
-#define ABORTED_COMMAND         0x0b
-#define MISCOMPARE              0x0e
-
-/* We want some additional flags for cd-rom drives.
-   To save space in the ide_drive_t struct, use some fields which
-   doesn't make sense for cd-roms -- `bios_sect' and `bios_head'. */
-
-/* Configuration flags.  These describe the capabilities of the drive.
-   They generally do not change after initialization, unless we learn
-   more about the drive from stuff failing. */
-struct ide_cd_config_flags {
-	__u8 drq_interrupt    : 1; /* Device sends an interrupt when ready
-				      for a packet command. */
-	__u8 no_doorlock      : 1; /* Drive cannot lock the door. */
-#if ! STANDARD_ATAPI
-	__u8 old_readcd       : 1; /* Drive uses old READ CD opcode. */
-	__u8 playmsf_as_bcd   : 1; /* PLAYMSF command takes BCD args. */
-	__u8 tocaddr_as_bcd   : 1; /* TOC addresses are in BCD. */
-	__u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
-	__u8 subchan_as_bcd   : 1; /* Subchannel info is in BCD. */
-#endif  /* not STANDARD_ATAPI */
-	__u8 reserved         : 1;
-};
-#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_sect))
-
- 
-/* State flags.  These give information about the current state of the
-   drive, and will change during normal operation. */
-struct ide_cd_state_flags {
-	__u8 media_changed : 1; /* Driver has noticed a media change. */
-	__u8 toc_valid     : 1; /* Saved TOC information is current. */
-	__u8 door_locked   : 1; /* We think that the drive door is locked. */
-	__u8 eject_on_close: 1; /* Drive should eject when device is closed. */
-	__u8 sanyo_slot    : 2; /* Sanyo 3 CD changer support */
-	__u8 reserved      : 2;
-};
-#define CDROM_STATE_FLAGS(drive)  ((struct ide_cd_state_flags *)&((drive)->bios_head))
-
-
-struct atapi_request_sense {
-	unsigned char error_code : 7;
-	unsigned char valid      : 1;
-	byte reserved1;
-	unsigned char sense_key  : 4;
-	unsigned char reserved2  : 1;
-	unsigned char ili        : 1;
-	unsigned char reserved3  : 2;
-	byte info[4];
-	byte sense_len;
-	byte command_info[4];
-	byte asc;
-	byte ascq;
-	byte fru;
-	byte sense_key_specific[3];
-};
-
-struct packet_command {
-	char *buffer;
-	int buflen;
-	int stat;
-	struct atapi_request_sense *sense_data;
-	unsigned char c[12];
-};
-
-
-/* Structure of a MSF cdrom address. */
-struct atapi_msf {
-	byte reserved;
-	byte minute;
-	byte second;
-	byte frame;
-};
-
-
-/* Space to hold the disk TOC. */
-
-#define MAX_TRACKS 99
-struct atapi_toc_header {
-	unsigned short toc_length;
-	byte first_track;
-	byte last_track;
-};
-
-struct atapi_toc_entry {
-	byte reserved1;
-	unsigned control : 4;
-	unsigned adr     : 4;
-	byte track;
-	byte reserved2;
-	union {
-		unsigned lba;
-		struct atapi_msf msf;
-	} addr;
-};
-
-struct atapi_toc {
-	int    last_session_lba;
-	int    xa_flag;
-	unsigned capacity;
-	struct atapi_toc_header hdr;
-	struct atapi_toc_entry  ent[MAX_TRACKS+1];
-	  /* One extra for the leadout. */
-};
-
-
-/* This structure is annoyingly close to, but not identical with,
-   the cdrom_subchnl structure from cdrom.h. */
-struct atapi_cdrom_subchnl 
-{
-	u_char  acdsc_reserved;
-	u_char  acdsc_audiostatus;
-	u_short acdsc_length;
-	u_char  acdsc_format;
-
-	u_char  acdsc_adr:	4;
-	u_char  acdsc_ctrl:	4;
-	u_char  acdsc_trk;
-	u_char  acdsc_ind;
-	union {
-		struct atapi_msf msf;
-		int	lba;
-	} acdsc_absaddr;
-	union {
-		struct atapi_msf msf;
-		int	lba;
-	} acdsc_reladdr;
-};
-
-
-/* Extra per-device info for cdrom drives. */
-struct cdrom_info {
-
-	/* Buffer for table of contents.  NULL if we haven't allocated
-	   a TOC buffer for this device yet. */
-
-	struct atapi_toc *toc;
-
-	/* Sector buffer.  If a read request wants only the first part
-	   of a cdrom block, we cache the rest of the block here,
-	   in the expectation that that data is going to be wanted soon.
-	   SECTOR_BUFFERED is the number of the first buffered sector,
-	   and NSECTORS_BUFFERED is the number of sectors in the buffer.
-	   Before the buffer is allocated, we should have
-	   SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
-
-	unsigned long sector_buffered;
-	unsigned long nsectors_buffered;
-	char *sector_buffer;
-
-	/* The result of the last successful request sense command
-	   on this device. */
-	struct atapi_request_sense sense_data;
-
-	struct request request_sense_request;
-	struct packet_command request_sense_pc;
-	int dma;
-};
-
-
-#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
-
-
-
 /****************************************************************************
- * Descriptions of ATAPI error codes.
+ * Generic packet command support and error handling routines.
  */
 
-#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
-
-#if VERBOSE_IDE_CD_ERRORS
-
-/* From Table 124 of the ATAPI 1.2 spec. */
-
-char *sense_key_texts[16] = {
-	"No sense data",
-	"Recovered error",
-	"Not ready",
-	"Medium error",
-	"Hardware error",
-	"Illegal request",
-	"Unit attention",
-	"Data protect",
-	"(reserved)",
-	"(reserved)",
-	"(reserved)",
-	"Aborted command",
-	"(reserved)",
-	"(reserved)",
-	"Miscompare",
-	"(reserved)",
-};
-
-
-/* From Table 125 of the ATAPI 1.2 spec. */
-
-struct {
-	short asc_ascq;
-	char *text;
-} sense_data_texts[] = {
-	{ 0x0000, "No additional sense information" },
-	{ 0x0011, "Audio play operation in progress" },
-	{ 0x0012, "Audio play operation paused" },
-	{ 0x0013, "Audio play operation successfully completed" },
-	{ 0x0014, "Audio play operation stopped due to error" },
-	{ 0x0015, "No current audio status to return" },
-
-	{ 0x0200, "No seek complete" },
-
-	{ 0x0400, "Logical unit not ready - cause not reportable" },
-	{ 0x0401,
-	  "Logical unit not ready - in progress (sic) of becoming ready" },
-	{ 0x0402, "Logical unit not ready - initializing command required" },
-	{ 0x0403, "Logical unit not ready - manual intervention required" },
-
-	{ 0x0600, "No reference position found" },
-
-	{ 0x0900, "Track following error" },
-	{ 0x0901, "Tracking servo failure" },
-	{ 0x0902, "Focus servo failure" },
-	{ 0x0903, "Spindle servo failure" },
-
-	{ 0x1100, "Unrecovered read error" },
-	{ 0x1106, "CIRC unrecovered error" },
-
-	{ 0x1500, "Random positioning error" },
-	{ 0x1501, "Mechanical positioning error" },
-	{ 0x1502, "Positioning error detected by read of medium" },
-
-	{ 0x1700, "Recovered data with no error correction applied" },
-	{ 0x1701, "Recovered data with retries" },
-	{ 0x1702, "Recovered data with positive head offset" },
-	{ 0x1703, "Recovered data with negative head offset" },
-	{ 0x1704, "Recovered data with retries and/or CIRC applied" },
-	{ 0x1705, "Recovered data using previous sector ID" },
-
-	{ 0x1800, "Recovered data with error correction applied" },
-	{ 0x1801, "Recovered data with error correction and retries applied" },
-	{ 0x1802, "Recovered data - the data was auto-reallocated" },
-	{ 0x1803, "Recovered data with CIRC" },
-	{ 0x1804, "Recovered data with L-EC" },
-	{ 0x1805, "Recovered data - recommend reassignment" },
-	{ 0x1806, "Recovered data - recommend rewrite" },
-
-	{ 0x1a00, "Parameter list length error" },
-
-	{ 0x2000, "Invalid command operation code" },
-
-	{ 0x2100, "Logical block address out of range" },
-
-	{ 0x2400, "Invalid field in command packet" },
-
-	{ 0x2600, "Invalid field in parameter list" },
-	{ 0x2601, "Parameter not supported" },
-	{ 0x2602, "Parameter value invalid" },
-	{ 0x2603, "Threshold parameters not supported" },
-
-	{ 0x2800, "Not ready to ready transition, medium may have changed" },
-
-	{ 0x2900, "Power on, reset or bus device reset occurred" },
 
-	{ 0x2a00, "Parameters changed" },
-	{ 0x2a01, "Mode parameters changed" },
-
-	{ 0x3000, "Incompatible medium installed" },
-	{ 0x3001, "Cannot read medium - unknown format" },
-	{ 0x3002, "Cannot read medium - incompatible format" },
-
-	{ 0x3700, "Rounded parameter" },
-
-	{ 0x3900, "Saving parameters not supported" },
-
-	{ 0x3a00, "Medium not present" },
-
-	{ 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
-	{ 0x3f01, "Microcode has been changed" },
-	{ 0x3f02, "Changed operating definition" },
-	{ 0x3f03, "Inquiry data has changed" },
-
-	{ 0x4000, "Diagnostic failure on component (ASCQ)" },
-
-	{ 0x4400, "Internal ATAPI CD-ROM drive failure" },
-
-	{ 0x4e00, "Overlapped commands attempted" },
-
-	{ 0x5300, "Media load or eject failed" },
-	{ 0x5302, "Medium removal prevented" },
-
-	{ 0x5700, "Unable to recover table of contents" },
-
-	{ 0x5a00, "Operator request or state change input (unspecified)" },
-	{ 0x5a01, "Operator medium removal request" },
-
-	{ 0x5b00, "Threshold condition met" },
-
-	{ 0x5c00, "Status change" },
-
-	{ 0x6300, "End of user area encountered on this track" },
-
-	{ 0x6400, "Illegal mode for this track" },
-
-	{ 0xbf00, "Loss of streaming" },
-};
-#endif
-
-
-
-/****************************************************************************
- * Generic packet command support and error handling routines.
- */
+/* Mark that we've seen a media change, and invalidate our internal
+   buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
+	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+	info->nsectors_buffered = 0;
+}
 
 
 static
@@ -534,15 +207,22 @@
 			       struct atapi_request_sense *reqbuf,
 			       struct packet_command *failed_command)
 {
-	/* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
-	   Workman (and probably other programs) uses this command to poll
-	   the drive, and we don't want to fill the syslog
-	   with useless errors. */
-	if (failed_command &&
-	    failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
-	    (reqbuf->sense_key == NOT_READY ||
-	     reqbuf->sense_key == UNIT_ATTENTION))
-		return;
+	if (reqbuf->sense_key == NOT_READY ||
+	    reqbuf->sense_key == UNIT_ATTENTION) {
+		/* Make good and sure we've seen this potential media change.
+		   Some drives (i.e. Creative) fail to present the correct
+		   sense key in the error register. */
+		cdrom_saw_media_change (drive);
+
+
+		/* Don't print not ready or unit attention errors for
+		   READ_SUBCHANNEL.  Workman (and probably other programs)
+		   uses this command to poll the drive, and we don't want
+		   to fill the syslog with useless errors. */
+		if (failed_command &&
+		    failed_command->c[0] == SCMD_READ_SUBCHANNEL)
+			return;
+	}
 
 #if VERBOSE_IDE_CD_ERRORS
 	{
@@ -726,18 +406,6 @@
 }
 
 
-/* Mark that we've seen a media change, and invalidate our internal
-   buffers. */
-static void cdrom_saw_media_change (ide_drive_t *drive)
-{
-	struct cdrom_info *info = drive->driver_data;
-	
-	CDROM_STATE_FLAGS (drive)->media_changed = 1;
-	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
-	info->nsectors_buffered = 0;
-}
-
-
 /* Returns 0 if the request should be continued.
    Returns 1 if the request was ended. */
 static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
@@ -793,7 +461,7 @@
 				   with this command, and we don't want
 				   to uselessly fill up the syslog. */
 				if (pc->c[0] != SCMD_READ_SUBCHANNEL)
-					printk ("%s : tray open or drive not ready\n",
+					printk ("%s: tray open or drive not ready\n",
 						drive->name);
 			} else if (sense_key == UNIT_ATTENTION) {
 				/* Check for media change. */
@@ -899,10 +567,11 @@
 	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
 	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
 	OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
-
+ 
 	if (info->dma)
 		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
 
+
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
 		ide_set_handler (drive, handler, WAIT_CMD);
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
@@ -947,7 +616,7 @@
 }
 
 
-
+
 /****************************************************************************
  * Block read functions.
  */
@@ -1065,7 +734,7 @@
 	}
 
 	if (cdrom_decode_status (drive, 0, &stat)) return;
-
+ 
 	if (dma) {
 		if (!dma_error) {
 			for (i = rq->nr_sectors; i > 0;) {
@@ -1077,6 +746,7 @@
 		return;
 	}
 
+
 	/* Read the interrupt reason and the transfer length. */
 	ireason = IN_BYTE (IDE_NSECTOR_REG);
 	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
@@ -1333,7 +1003,7 @@
 
 
 
-
+
 /****************************************************************************
  * Execute all other packet commands.
  */
@@ -1557,7 +1227,7 @@
 	}
 }
 
-
+
 /****************************************************************************
  * cdrom driver request routine.
  */
@@ -1578,7 +1248,7 @@
 }
 
 
-
+
 /****************************************************************************
  * Ioctl handling.
  *
@@ -1644,10 +1314,12 @@
 	pc.sense_data = reqbuf;
 	pc.c[0] = TEST_UNIT_READY;
 
+#if ! STANDARD_ATAPI
         /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
            switch CDs instead of supporting the LOAD_UNLOAD opcode   */
 
         pc.c[7] = CDROM_STATE_FLAGS (drive)->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
 
 	return cdrom_queue_packet_command (drive, &pc);
 }
@@ -1677,21 +1349,21 @@
 		stat = cdrom_queue_packet_command (drive, &pc);
 	}
 
+	/* If we got an illegal field error, the drive
+	   probably cannot lock the door. */
+	if (stat != 0 &&
+	    reqbuf->sense_key == ILLEGAL_REQUEST &&
+	    reqbuf->asc == 0x24) {
+		printk ("%s: door locking not supported\n",
+			drive->name);
+		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+		stat = 0;
+	}
+
 	if (stat == 0)
 		CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
-	else {
-		/* If we got an illegal field error, the drive
-		   probably cannot lock the door. */
-		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
-		    reqbuf->asc == 0x24) {
-			printk ("%s: door locking not supported\n",
-				drive->name);
-			CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
-			stat = 0;
-			CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
-		}
-	}
-  return stat;
+
+	return stat;
 }
 
 
@@ -1918,7 +1590,7 @@
 	pc.c[0] = SCMD_READ_SUBCHANNEL;
 	pc.c[1] = 2;     /* MSF addressing */
 	pc.c[2] = 0x40;  /* request subQ data */
-	pc.c[3] = format,
+	pc.c[3] = format;
 	pc.c[7] = (buflen >> 8);
 	pc.c[8] = (buflen & 0xff);
 	return cdrom_queue_packet_command (drive, &pc);
@@ -2064,7 +1736,6 @@
 {
 	struct packet_command pc;
 	struct atapi_request_sense my_reqbuf;
-	int stat;
 
 	if (reqbuf == NULL)
 		reqbuf = &my_reqbuf;
@@ -2076,7 +1747,7 @@
 	pc.buflen = buflen;
 
 #if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->old_readcd)
+	if (CDROM_CONFIG_FLAGS (drive)->nec260)
 		pc.c[0] = 0xd4;
 	else
 #endif  /* not STANDARD_ATAPI */
@@ -2092,24 +1763,7 @@
 	else
 		pc.c[9] = 0x10;
 
-	stat = cdrom_queue_packet_command (drive, &pc);
-
-#if ! STANDARD_ATAPI
-	/* If the drive doesn't recognize the READ CD opcode, retry the command
-	   with an older opcode for that command. */
-	if (stat && reqbuf->sense_key == ILLEGAL_REQUEST &&
-	    reqbuf->asc == 0x20 &&
-	    CDROM_CONFIG_FLAGS (drive)->old_readcd == 0) {
-		printk ("%s: Drive does not recognize READ_CD;"
-			"trying opcode 0xd4\n",
-			drive->name);
-		CDROM_CONFIG_FLAGS (drive)->old_readcd = 1;
-		return cdrom_read_block (drive, format, lba, nblocks,
-					 buf, buflen, reqbuf);
-	}
-#endif  /* not STANDARD_ATAPI */
-
-	return stat;
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -2118,23 +1772,26 @@
 cdrom_load_unload (ide_drive_t *drive, int slot,
 		   struct atapi_request_sense *reqbuf)
 {
+#if ! STANDARD_ATAPI
 	/* if the drive is a Sanyo 3 CD changer then TEST_UNIT_READY
            (used in the cdrom_check_status function) is used to 
            switch CDs instead of LOAD_UNLOAD */
 
 	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
 
-        	if ((slot == 1) || (slot == 2)) {
+        	if ((slot == 1) || (slot == 2))
 			CDROM_STATE_FLAGS (drive)->sanyo_slot = slot;
-		} else if (slot >= 0) {
+		else if (slot >= 0)
 			CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
-		} else {
+		else
 			return 0;
-		}
 
-		return cdrom_check_status (drive, NULL);
+		return cdrom_check_status (drive, reqbuf);
 
-	} else {
+	}
+	else
+#endif /*not STANDARD_ATAPI */
+	{
 
 		/* ATAPI Rev. 2.2+ standard for requesting switching of
                    CDs in a multiplatter device */
@@ -2153,169 +1810,299 @@
 }
 
 
-int ide_cdrom_ioctl (ide_drive_t *drive, struct inode *inode,
-		     struct file *file, unsigned int cmd, unsigned long arg)
+/* This gets the mechanism status per ATAPI draft spec 2.6 */
+static int
+cdrom_read_mech_status (ide_drive_t *drive, char *buf, int buflen,
+			struct atapi_request_sense *reqbuf)
 {
-	struct cdrom_info *info = drive->driver_data;
-	
-	switch (cmd) {
-	case CDROMEJECT: {
-		int stat;
+	struct packet_command pc;
 
-		if (drive->usage > 1)
-			return -EBUSY;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-		stat = cdrom_lockdoor (drive, 0, NULL);
-		if (stat) return stat;
+	pc.buffer = buf;
+	pc.buflen = buflen;
+	pc.c[0] = MECHANISM_STATUS;
+	pc.c[8] = (buflen >> 8);
+	pc.c[9] = (buflen & 0xff);
+	return cdrom_queue_packet_command (drive, &pc);
+}
 
-		return cdrom_eject (drive, 0, NULL);
-	}
 
-	case CDROMCLOSETRAY: {
+/* Read the drive mechanism status and slot table into our internal buffer.
+   If the buffer does not yet exist, allocate it. */
+static int
+cdrom_read_changer_info (ide_drive_t *drive)
+{
+	int nslots;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (info->changer_info)
+		nslots = info->changer_info->hdr.nslots;
+
+	else {
+		struct atapi_mechstat_header mechbuf;
 		int stat;
-		if (drive->usage > 1)
-			return -EBUSY;
 
-		stat = cdrom_eject (drive, 1, NULL);
-		if (stat) return stat;
+		stat = cdrom_read_mech_status (drive,
+					       (char *)&mechbuf,
+					       sizeof (mechbuf),
+					       NULL);
+		if (stat)
+			return stat;
 
-		return cdrom_lockdoor (drive, 1, NULL);
-	}
+		nslots = mechbuf.nslots;
+		info->changer_info =
+			(struct atapi_changer_info *)
+			kmalloc (sizeof (struct atapi_changer_info) +
+				 nslots * sizeof (struct atapi_slot),
+				 GFP_KERNEL);
 
-	case CDROMEJECT_SW: {
-		CDROM_STATE_FLAGS (drive)->eject_on_close = arg;
-		return 0;
+		if (info->changer_info == NULL)
+			return -ENOMEM;
 	}
 
-	case CDROMPAUSE:
-		return cdrom_pause (drive, 1, NULL);
-
-	case CDROMRESUME:
-		return cdrom_pause (drive, 0, NULL);
+	return cdrom_read_mech_status
+		(drive,
+		 (char *)&info->changer_info->hdr,
+		 sizeof (struct atapi_mechstat_header) +
+		 nslots * sizeof (struct atapi_slot),
+		 NULL);
+}
 
-	case CDROMSTART:
-		return cdrom_startstop (drive, 1, NULL);
 
-	case CDROMSTOP: {
-		int stat;
+static
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+			 unsigned int cmd, unsigned long arg)
+			 
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-		stat = cdrom_startstop (drive, 0, NULL);
-		if (stat) return stat;
-		/* pit says the Dolphin needs this. */
-		return cdrom_eject (drive, 1, NULL);
-      }
 
-	case CDROMPLAYMSF: {
+	switch (cmd) {
+	case CDROMREADMODE1:
+	case CDROMREADMODE2: {
 		struct cdrom_msf msf;
-		int stat, lba_start, lba_end;
+		int blocksize, format, stat, lba;
+		struct atapi_toc *toc;
+		char *buf;
+
+		if (cmd == CDROMREADMODE1) {
+			blocksize = CD_FRAMESIZE;
+			format = 2;
+		} else {
+			blocksize = CD_FRAMESIZE_RAW0;
+			format = 3;
+		}
 
-		stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
+		stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
 		if (stat) return stat;
 
-		copy_from_user (&msf, (void *) arg, sizeof(msf));
+		copy_from_user (&msf, (void *)arg, sizeof (msf));
 
-		lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
-					msf.cdmsf_frame0);
-		lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
-				      msf.cdmsf_frame1) + 1;
+		lba = msf_to_lba (msf.cdmsf_min0,
+				  msf.cdmsf_sec0,
+				  msf.cdmsf_frame0);
+	
+		/* Make sure the TOC is up to date. */
+		stat = cdrom_read_toc (drive, NULL);
+		if (stat) return stat;
 
-		if (lba_end <= lba_start) return -EINVAL;
+		toc = info->toc;
 
-		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+		if (lba < 0 || lba >= toc->capacity)
+			return -EINVAL;
+
+		buf = (char *) kmalloc (CD_FRAMESIZE_RAW0, GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+
+		stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize,
+					 NULL);
+		if (stat == 0)
+			copy_to_user ((char *)arg, buf, blocksize);
+
+		kfree (buf);
+		return stat;
 	}
 
-	/* Like just about every other Linux cdrom driver, we ignore the
-	   index part of the request here. */
-	case CDROMPLAYTRKIND: {
-		int stat, lba_start, lba_end;
-		struct cdrom_ti ti;
-		struct atapi_toc_entry *first_toc, *last_toc;
+	/* Read 2352 byte blocks from audio tracks. */
+	case CDROMREADAUDIO: {
+		int stat, lba;
+		struct atapi_toc *toc;
+		struct cdrom_read_audio ra;
+		char *buf;
 
-		stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
+		/* Make sure the TOC is up to date. */
+		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
 
-		copy_from_user (&ti, (void *) arg, sizeof(ti));
+		toc = info->toc;
 
-		stat = cdrom_get_toc_entry (drive, ti.cdti_trk0, &first_toc,
-					    NULL);
-		if (stat) return stat;
-		stat = cdrom_get_toc_entry (drive, ti.cdti_trk1, &last_toc,
-					    NULL);
+		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
 		if (stat) return stat;
 
-		if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
-		lba_start = first_toc->addr.lba;
-		lba_end   = last_toc->addr.lba;
+		copy_from_user (&ra, (void *)arg, sizeof (ra));
 
-		if (lba_end <= lba_start) return -EINVAL;
+		if (ra.nframes < 0 || ra.nframes > toc->capacity)
+			return -EINVAL;
+		else if (ra.nframes == 0)
+			return 0;
 
-		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
-	}
+		stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
+				    ra.nframes * CD_FRAMESIZE_RAW);
+		if (stat) return stat;
 
-	case CDROMREADTOCHDR: {
-		int stat;
-		struct cdrom_tochdr tochdr;
-		struct atapi_toc *toc;
+		if (ra.addr_format == CDROM_MSF)
+			lba = msf_to_lba (ra.addr.msf.minute,
+					  ra.addr.msf.second,
+					  ra.addr.msf.frame);
+		else if (ra.addr_format == CDROM_LBA)
+			lba = ra.addr.lba;
+		else
+			return -EINVAL;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (tochdr));
-		if (stat) return stat;
+		if (lba < 0 || lba >= toc->capacity)
+			return -EINVAL;
 
-		/* Make sure our saved TOC is valid. */
-		stat = cdrom_read_toc (drive, NULL);
-		if (stat) return stat;
+		buf = (char *) kmalloc (CDROM_NBLOCKS_BUFFER*CD_FRAMESIZE_RAW,
+					GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
 
-		toc = info->toc;
-		tochdr.cdth_trk0 = toc->hdr.first_track;
-		tochdr.cdth_trk1 = toc->hdr.last_track;
+		while (ra.nframes > 0) {
+			int this_nblocks = ra.nframes;
+			if (this_nblocks > CDROM_NBLOCKS_BUFFER)
+				this_nblocks = CDROM_NBLOCKS_BUFFER;
+			stat = cdrom_read_block
+				(drive, 1, lba, this_nblocks,
+				 buf, this_nblocks * CD_FRAMESIZE_RAW, NULL);
+			if (stat) break;
 
-		copy_to_user ((void *) arg, &tochdr, sizeof (tochdr));
+			copy_to_user (ra.buf, buf,
+				     this_nblocks * CD_FRAMESIZE_RAW);
+			ra.buf += this_nblocks * CD_FRAMESIZE_RAW;
+			ra.nframes -= this_nblocks;
+			lba += this_nblocks;
+		}
 
+		kfree (buf);
 		return stat;
 	}
 
-	case CDROMREADTOCENTRY: {
+ 	case CDROMSETSPINDOWN: {
+ 		char spindown;
+ 		char buffer[16];
+ 		int stat;
+ 
+ 		stat = verify_area (VERIFY_READ, (void *) arg,
+ 				    sizeof (char));
+ 		if (stat) return stat;
+ 
+ 		copy_from_user (&spindown, (void *) arg, sizeof(char));
+ 
+ 		stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
+ 					 sizeof (buffer), NULL);
+ 		if (stat) return stat;
+
+ 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+ 		return cdrom_mode_select (drive, PAGE_CDROM, buffer,
+ 					  sizeof (buffer), NULL);			
+ 	} 
+ 
+ 	case CDROMGETSPINDOWN: {
+ 		char spindown;
+ 		char buffer[16];
+ 		int stat;
+ 
+ 		stat = verify_area (VERIFY_WRITE, (void *) arg,
+                                    sizeof (char));
+ 		if (stat) return stat;
+ 
+ 		stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
+                                         sizeof (buffer), NULL);
+ 		if (stat) return stat;
+ 
+ 		spindown = buffer[11] & 0x0f;
+ 
+ 		copy_to_user ((void *) arg, &spindown, sizeof (char));
+ 
+ 		return 0;
+ 	}
+  
+#ifdef ALLOW_TEST_PACKETS
+	case 0x1234: {
 		int stat;
-		struct cdrom_tocentry tocentry;
-		struct atapi_toc_entry *toce;
+		struct packet_command pc;
+		int len, lena;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (tocentry));
-		if (stat) return stat;
+		memset (&pc, 0, sizeof (pc));
 
-		copy_from_user (&tocentry, (void *) arg, sizeof (tocentry));
+		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
+		if (stat) return stat;
+		copy_from_user (&pc.c, (void *) arg, sizeof (pc.c));
+		arg += sizeof (pc.c);
 
-		stat = cdrom_get_toc_entry (drive, tocentry.cdte_track, &toce,
-					    NULL);
+		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
 		if (stat) return stat;
+		copy_from_user (&len, (void *) arg , sizeof (len));
+		arg += sizeof (len);
 
-		tocentry.cdte_ctrl = toce->control;
-		tocentry.cdte_adr  = toce->adr;
+		lena = len;
+		if (lena  < 0) lena = -lena;
 
-		if (tocentry.cdte_format == CDROM_MSF) {
-			/* convert to MSF */
-			lba_to_msf (toce->addr.lba,
-				    &tocentry.cdte_addr.msf.minute,
-				    &tocentry.cdte_addr.msf.second,
-				    &tocentry.cdte_addr.msf.frame);
-		} else
-			tocentry.cdte_addr.lba = toce->addr.lba;
+		{
+			char buf[lena];
+			if (len > 0) {
+				stat = verify_area (VERIFY_WRITE,
+						    (void *) arg, len);
+				if (stat) return stat;
+			}
+			else if (len < 0) {
+				stat = verify_area (VERIFY_READ,
+						    (void *) arg, -len);
+				if (stat) return stat;
+				copy_from_user (buf, (void*)arg, -len);
+			}
+
+			if (len != 0) {
+				pc.buflen = len;
+				pc.buffer = buf;
+			}
 
-		copy_to_user ((void *) arg, &tocentry, sizeof (tocentry));
+			stat = cdrom_queue_packet_command (drive, &pc);
+
+			if (len > 0)
+				copy_to_user ((void *)arg, buf, len);
+		}
 
 		return stat;
 	}
+#endif
+
+	default:
+		return -EINVAL;
+	}
+
+}
+
+
+
+static
+int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
+			   unsigned int cmd, void *arg)
+			   
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
+	switch (cmd) {
 	case CDROMSUBCHNL: {
 		struct atapi_cdrom_subchnl scbuf;
 		int stat;
-		struct cdrom_subchnl subchnl;
-
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (subchnl));
-		if (stat) return stat;
-
-		copy_from_user (&subchnl, (void *) arg, sizeof (subchnl));
+		struct cdrom_subchnl *subchnl = (struct cdrom_subchnl *)arg;
 
 		stat = cdrom_read_subchannel (drive, 1, /* current position */
 					      (char *)&scbuf, sizeof (scbuf),
@@ -2331,401 +2118,427 @@
 			scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
 #endif /* not STANDARD_ATAPI */
 
-		if (subchnl.cdsc_format == CDROM_MSF) {
-			subchnl.cdsc_absaddr.msf.minute =
-				scbuf.acdsc_absaddr.msf.minute;
-			subchnl.cdsc_absaddr.msf.second =
-				scbuf.acdsc_absaddr.msf.second;
-			subchnl.cdsc_absaddr.msf.frame =
-				scbuf.acdsc_absaddr.msf.frame;
-
-			subchnl.cdsc_reladdr.msf.minute =
-				scbuf.acdsc_reladdr.msf.minute;
-			subchnl.cdsc_reladdr.msf.second =
-				scbuf.acdsc_reladdr.msf.second;
-			subchnl.cdsc_reladdr.msf.frame =
-				scbuf.acdsc_reladdr.msf.frame;
-		} else {
-			subchnl.cdsc_absaddr.lba =
-				msf_to_lba (scbuf.acdsc_absaddr.msf.minute,
-					    scbuf.acdsc_absaddr.msf.second,
-					    scbuf.acdsc_absaddr.msf.frame);
-			subchnl.cdsc_reladdr.lba =
-				msf_to_lba (scbuf.acdsc_reladdr.msf.minute,
-					    scbuf.acdsc_reladdr.msf.second,
-					    scbuf.acdsc_reladdr.msf.frame);
-		}
-
-		subchnl.cdsc_audiostatus = scbuf.acdsc_audiostatus;
-		subchnl.cdsc_ctrl = scbuf.acdsc_ctrl;
-		subchnl.cdsc_trk  = scbuf.acdsc_trk;
-		subchnl.cdsc_ind  = scbuf.acdsc_ind;
+		subchnl->cdsc_absaddr.msf.minute =
+			scbuf.acdsc_absaddr.msf.minute;
+		subchnl->cdsc_absaddr.msf.second =
+			scbuf.acdsc_absaddr.msf.second;
+		subchnl->cdsc_absaddr.msf.frame =
+			scbuf.acdsc_absaddr.msf.frame;
+
+		subchnl->cdsc_reladdr.msf.minute =
+			scbuf.acdsc_reladdr.msf.minute;
+		subchnl->cdsc_reladdr.msf.second =
+			scbuf.acdsc_reladdr.msf.second;
+		subchnl->cdsc_reladdr.msf.frame =
+			scbuf.acdsc_reladdr.msf.frame;
+
+		subchnl->cdsc_audiostatus = scbuf.acdsc_audiostatus;
+		subchnl->cdsc_ctrl = scbuf.acdsc_ctrl;
+		subchnl->cdsc_trk  = scbuf.acdsc_trk;
+		subchnl->cdsc_ind  = scbuf.acdsc_ind;
 
-		copy_to_user ((void *) arg, &subchnl, sizeof (subchnl));
-
-		return stat;
+		return 0;
 	}
 
-	case CDROMVOLCTRL: {
-		struct cdrom_volctrl volctrl;
-		char buffer[24], mask[24];
+	case CDROMREADTOCHDR: {
 		int stat;
+		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+		struct atapi_toc *toc;
 
-		stat = verify_area (VERIFY_READ, (void *) arg,
-				    sizeof (volctrl));
+		/* Make sure our saved TOC is valid. */
+		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
-		copy_from_user (&volctrl, (void *) arg, sizeof (volctrl));
 
-		stat = cdrom_mode_sense (drive, 0x0e, 0, buffer,
-					 sizeof (buffer), NULL);
-		if (stat) return stat;
-		stat = cdrom_mode_sense (drive, 0x0e, 1, mask,
-					 sizeof (buffer), NULL);
+		toc = info->toc;
+		tochdr->cdth_trk0 = toc->hdr.first_track;
+		tochdr->cdth_trk1 = toc->hdr.last_track;
+
+		return 0;
+	}
+
+	case CDROMREADTOCENTRY: {
+		int stat;
+		struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
+		struct atapi_toc_entry *toce;
+
+		stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce,
+					    NULL);
 		if (stat) return stat;
 
-		buffer[1] = buffer[2] = 0;
+		tocentry->cdte_ctrl = toce->control;
+		tocentry->cdte_adr  = toce->adr;
+		tocentry->cdte_format = CDROM_LBA;
+		tocentry->cdte_addr.lba = toce->addr.lba;
+
+		return 0;
+	}
 
-		buffer[17] = volctrl.channel0 & mask[17];
-		buffer[19] = volctrl.channel1 & mask[19];
-		buffer[21] = volctrl.channel2 & mask[21];
-		buffer[23] = volctrl.channel3 & mask[23];
+	case CDROMPLAYMSF: {
+		struct cdrom_msf *msf = (struct cdrom_msf *) arg;
+		int lba_start, lba_end;
 
-		return cdrom_mode_select (drive, 0x0e, buffer,
-					  sizeof (buffer), NULL);
+		lba_start = msf_to_lba (msf->cdmsf_min0, msf->cdmsf_sec0,
+					msf->cdmsf_frame0);
+		lba_end = msf_to_lba (msf->cdmsf_min1, msf->cdmsf_sec1,
+				      msf->cdmsf_frame1) + 1;
+
+		if (lba_end <= lba_start) return -EINVAL;
+
+		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
 	}
 
-	case CDROMVOLREAD: {
-		struct cdrom_volctrl volctrl;
-		char buffer[24];
-		int stat;
+	/* Like just about every other Linux cdrom driver, we ignore the
+	   index part of the request here. */
+	case CDROMPLAYTRKIND: {
+		int stat, lba_start, lba_end;
+		struct cdrom_ti *ti = (struct cdrom_ti *)arg;
+		struct atapi_toc_entry *first_toc, *last_toc;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (volctrl));
+		stat = cdrom_get_toc_entry (drive, ti->cdti_trk0, &first_toc,
+					    NULL);
 		if (stat) return stat;
-
-		stat = cdrom_mode_sense (drive, 0x0e, 0, buffer,
-					 sizeof (buffer), NULL);
+		stat = cdrom_get_toc_entry (drive, ti->cdti_trk1, &last_toc,
+					    NULL);
 		if (stat) return stat;
 
-		volctrl.channel0 = buffer[17];
-		volctrl.channel1 = buffer[19];
-		volctrl.channel2 = buffer[21];
-		volctrl.channel3 = buffer[23];
+		if (ti->cdti_trk1 != CDROM_LEADOUT) ++last_toc;
+		lba_start = first_toc->addr.lba;
+		lba_end   = last_toc->addr.lba;
 
-		copy_to_user ((void *) arg, &volctrl, sizeof (volctrl));
+		if (lba_end <= lba_start) return -EINVAL;
 
-		return 0;
+		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
 	}
 
-	case CDROMMULTISESSION: {
-		struct cdrom_multisession ms_info;
-		struct atapi_toc *toc;
+	case CDROMVOLCTRL: {
+		struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
+		char buffer[24], mask[24];
 		int stat;
 
-		stat = verify_area (VERIFY_WRITE, (void *)arg,
-				    sizeof (ms_info));
+		stat = cdrom_mode_sense (drive, PAGE_AUDIO, 0, buffer,
+					 sizeof (buffer), NULL);
+		if (stat) return stat;
+		stat = cdrom_mode_sense (drive, PAGE_AUDIO, 1, mask,
+					 sizeof (buffer), NULL);
 		if (stat) return stat;
 
-		copy_from_user (&ms_info, (void *)arg, sizeof (ms_info));
+		buffer[1] = buffer[2] = 0;
 
-		/* Make sure the TOC information is valid. */
-		stat = cdrom_read_toc (drive, NULL);
-		if (stat) return stat;
+		buffer[17] = volctrl->channel0 & mask[17];
+		buffer[19] = volctrl->channel1 & mask[19];
+		buffer[21] = volctrl->channel2 & mask[21];
+		buffer[23] = volctrl->channel3 & mask[23];
 
-		toc = info->toc;
+		return cdrom_mode_select (drive, PAGE_AUDIO, buffer,
+					  sizeof (buffer), NULL);
+	}
 
-		if (ms_info.addr_format == CDROM_MSF)
-			lba_to_msf (toc->last_session_lba,
-				    &ms_info.addr.msf.minute,
-				    &ms_info.addr.msf.second,
-				    &ms_info.addr.msf.frame);
-		else if (ms_info.addr_format == CDROM_LBA)
-			ms_info.addr.lba = toc->last_session_lba;
-		else
-			return -EINVAL;
+	case CDROMVOLREAD: {
+		struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
+		char buffer[24];
+		int stat;
 
-		ms_info.xa_flag = toc->xa_flag;
+		stat = cdrom_mode_sense (drive, PAGE_AUDIO, 0, buffer,
+					 sizeof (buffer), NULL);
+		if (stat) return stat;
 
-		copy_to_user ((void *)arg, &ms_info, sizeof (ms_info));
+		volctrl->channel0 = buffer[17];
+		volctrl->channel1 = buffer[19];
+		volctrl->channel2 = buffer[21];
+		volctrl->channel3 = buffer[23];
 
 		return 0;
 	}
 
-	/* Read 2352 byte blocks from audio tracks. */
-	case CDROMREADAUDIO: {
-		int stat, lba;
-		struct atapi_toc *toc;
-		struct cdrom_read_audio ra;
-		char *buf;
-
-		/* Make sure the TOC is up to date. */
-		stat = cdrom_read_toc (drive, NULL);
-		if (stat) return stat;
+	case CDROMSTART:
+		return cdrom_startstop (drive, 1, NULL);
 
-		toc = info->toc;
+	case CDROMSTOP: {
+		int stat;
 
-		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
+		stat = cdrom_startstop (drive, 0, NULL);
 		if (stat) return stat;
+		/* pit says the Dolphin needs this. */
+		return cdrom_eject (drive, 1, NULL);
+	}
 
-		copy_from_user (&ra, (void *)arg, sizeof (ra));
+	case CDROMPAUSE:
+		return cdrom_pause (drive, 1, NULL);
 
-		if (ra.nframes < 0 || ra.nframes > toc->capacity)
-			return -EINVAL;
-		else if (ra.nframes == 0)
-			return 0;
+	case CDROMRESUME:
+		return cdrom_pause (drive, 0, NULL);
 
-		stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
-				    ra.nframes * CD_FRAMESIZE_RAW);
-		if (stat) return stat;
 
-		if (ra.addr_format == CDROM_MSF)
-			lba = msf_to_lba (ra.addr.msf.minute,
-					  ra.addr.msf.second,
-					  ra.addr.msf.frame);
-		else if (ra.addr_format == CDROM_LBA)
-			lba = ra.addr.lba;
-		else
-			return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+}
 
-		if (lba < 0 || lba >= toc->capacity)
-			return -EINVAL;
 
-		buf = (char *) kmalloc (CDROM_NBLOCKS_BUFFER*CD_FRAMESIZE_RAW,
-					GFP_KERNEL);
-		if (buf == NULL)
-			return -ENOMEM;
+static
+int ide_cdrom_reset (struct cdrom_device_info *cdi)
+{
 
-		while (ra.nframes > 0) {
-			int this_nblocks = ra.nframes;
-			if (this_nblocks > CDROM_NBLOCKS_BUFFER)
-				this_nblocks = CDROM_NBLOCKS_BUFFER;
-			stat = cdrom_read_block
-				(drive, 1, lba, this_nblocks,
-				 buf, this_nblocks * CD_FRAMESIZE_RAW, NULL);
-			if (stat) break;
+/* This doesn't work reliably yet, and so it is currently just a stub. */
 
-			copy_to_user (ra.buf, buf,
-				     this_nblocks * CD_FRAMESIZE_RAW);
-			ra.buf += this_nblocks * CD_FRAMESIZE_RAW;
-			ra.nframes -= this_nblocks;
-			lba += this_nblocks;
-		}
+#if 0 
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct request req;
+	ide_init_drive_cmd (&req);
+	req.cmd = RESET_DRIVE_COMMAND;
+	return ide_do_drive_cmd (drive, &req, ide_wait);
+#endif
 
-		kfree (buf);
-		return stat;
-	}
+/* For now, just return 0, as if things worked...	*/
+	return 0;
 
-	case CDROMREADMODE1:
-	case CDROMREADMODE2: {
-		struct cdrom_msf msf;
-		int blocksize, format, stat, lba;
-		struct atapi_toc *toc;
-		char *buf;
 
-		if (cmd == CDROMREADMODE1) {
-			blocksize = CD_FRAMESIZE;
-			format = 2;
-		} else {
-			blocksize = CD_FRAMESIZE_RAW0;
-			format = 3;
-		}
+}
 
-		stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
-		if (stat) return stat;
 
-		copy_from_user (&msf, (void *)arg, sizeof (msf));
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 
-		lba = msf_to_lba (msf.cdmsf_min0,
-				  msf.cdmsf_sec0,
-				  msf.cdmsf_frame0);
-	
-		/* Make sure the TOC is up to date. */
-		stat = cdrom_read_toc (drive, NULL);
+	if (position) {
+		int stat = cdrom_lockdoor (drive, 0, NULL);
 		if (stat) return stat;
+	}
 
-		toc = info->toc;
-
-		if (lba < 0 || lba >= toc->capacity)
-			return -EINVAL;
-
-		buf = (char *) kmalloc (CD_FRAMESIZE_RAW0, GFP_KERNEL);
-		if (buf == NULL)
-			return -ENOMEM;
+	return cdrom_eject (drive, !position, NULL);
+}
 
-		stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize,
-					 NULL);
-		if (stat == 0)
-			copy_to_user ((char *)arg, buf, blocksize);
 
-		kfree (buf);
-		return stat;
-	}
+static
+int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	return cdrom_lockdoor (drive, lock, NULL);
+}
 
-	case CDROM_GET_UPC: {
-		int stat;
-		char mcnbuf[24];
-		struct cdrom_mcn mcn;
 
-		stat = verify_area (VERIFY_WRITE, (void *) arg,
-				    sizeof (mcn));
-		if (stat) return stat;
+static
+int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-		stat = cdrom_read_subchannel (drive, 2, /* get MCN */
-					      mcnbuf, sizeof (mcnbuf),
-					      NULL);
-		if (stat) return stat;
+	struct atapi_request_sense my_reqbuf;
+	int stat;
+	int nslots, curslot;
 
-		memcpy (mcn.medium_catalog_number, mcnbuf+9,
-			sizeof (mcn.medium_catalog_number)-1);
-		mcn.medium_catalog_number[sizeof (mcn.medium_catalog_number)-1]
-			= '\0';
+	if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) {
+		printk ("%s: Not a changer.", drive->name);
+		return -EINVAL;
+	}
 
-		copy_to_user ((void *) arg, &mcn, sizeof (mcn));
+#if ! STANDARD_ATAPI
+	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+		nslots = 3;
+		curslot = CDROM_STATE_FLAGS (drive)->sanyo_slot;
+		if (curslot == 3)
+			curslot = 0;
+	}
+	else
+#endif /* not STANDARD_ATAPI */
+	{
+		stat = cdrom_read_changer_info (drive);
+		if (stat)
+			return stat;
 
-		return stat;
+		nslots = info->changer_info->hdr.nslots;
+		curslot = info->changer_info->hdr.curslot;
 	}
 
-	case CDROMLOADFROMSLOT:
-		printk ("%s: Use CDROM_SELECT_DISC "
-			" instead of CDROMLOADFROMSLOT.\n", drive->name);
-		/* Fall through. */
+	if (slot == curslot)
+		return curslot;
 
-	case CDROM_SELECT_DISC: {
-		struct atapi_request_sense my_reqbuf;
-		int stat;
+	if (slot == CDSL_CURRENT)
+		return curslot;
 
-		if (drive->usage > 1)
-			return -EBUSY;
+	if (slot != CDSL_NONE && (slot < 0 || slot >= nslots))
+		return -EINVAL;
 
-		(void) cdrom_load_unload (drive, -1, NULL);
+	if (drive->usage > 1)
+		return -EBUSY;
 
-                cdrom_saw_media_change (drive);
-                if (arg == -1) {
-			(void) cdrom_lockdoor (drive, 0, NULL);
-			return 0;
+	stat = cdrom_check_status (drive, &my_reqbuf);
+	if (stat && my_reqbuf.sense_key == NOT_READY)
+		return (-ENOENT);
+
+	if (slot == CDSL_NONE) {
+		(void) cdrom_load_unload (drive, -1, NULL);
+		cdrom_saw_media_change (drive);
+		(void) cdrom_lockdoor (drive, 0, NULL);
+		return 0;
+	}
+	else {
+		if (
+#if ! STANDARD_ATAPI
+		    CDROM_STATE_FLAGS (drive)->sanyo_slot == 0 &&
+#endif
+		    info->changer_info->slots[slot].disc_present
+		    == 0) {
+			printk ("%s: Requested slot does not contain a CD.\n",
+				drive->name);
+			return (-ENOENT);
 		}
-		(void) cdrom_load_unload (drive, (int)arg, NULL);
 
+		stat = cdrom_load_unload (drive, slot, NULL);
+		cdrom_saw_media_change (drive);
+		if (stat)
+			return stat;
+			
 		stat = cdrom_check_status (drive, &my_reqbuf);
-		if (stat && my_reqbuf.sense_key == NOT_READY) {
+		if (stat && my_reqbuf.sense_key == NOT_READY)
 			return -ENOENT;
-		}
 
-		/* And try to read the TOC information now. */
-		return cdrom_read_toc (drive, &my_reqbuf);
+		if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) {
+			stat = cdrom_read_toc (drive, &my_reqbuf);
+			if (stat)
+				return stat;
+			return slot;
+		}
+		else
+			return stat;
 	}
+}
 
-#if 0 /* Doesn't work reliably yet. */
-	case CDROMRESET: {
-		struct request req;
-		ide_init_drive_cmd (&req);
-		req.cmd = RESET_DRIVE_COMMAND;
-		return ide_do_drive_cmd (drive, &req, ide_wait);
+
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (slot_nr == CDSL_CURRENT) {
+
+		struct atapi_request_sense my_reqbuf;
+		int stat = cdrom_check_status (drive, &my_reqbuf);
+		if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION)
+			return CDS_DISC_OK;
+
+		if (my_reqbuf.sense_key == NOT_READY) {
+			/* With my NEC260, at least, we can't distinguish
+			   between tray open and tray closed but no disc
+			   inserted. */
+			return CDS_TRAY_OPEN; 
+		}
+
+		return CDS_DRIVE_NOT_READY;
 	}
-#endif
 
- 
-#ifdef TEST
-	case 0x1234: {
-		int stat;
-		struct packet_command pc;
-		int len, lena;
+#if ! STANDARD_ATAPI
+	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0)
+		return CDS_NO_INFO;
+#endif /* not STANDARD_ATAPI */
 
-		memset (&pc, 0, sizeof (pc));
+	else {
+		struct atapi_changer_info *ci;
+		int stat = cdrom_read_changer_info (drive);
+		if (stat < 0)
+			return stat;
+		ci = info->changer_info;
 
-		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
-		if (stat) return stat;
-		copy_from_user (&pc.c, (void *) arg, sizeof (pc.c));
-		arg += sizeof (pc.c);
+		if (ci->slots[slot_nr].disc_present)
+			return CDS_DISC_OK;
+		else
+			return CDS_NO_DISC;
+	}
+}
 
-		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
-		if (stat) return stat;
-		copy_from_user (&len, (void *) arg , sizeof (len));
-		arg += sizeof (len);
 
-		if (len > 0) {
-			stat = verify_area (VERIFY_WRITE, (void *) arg, len);
-			if (stat) return stat;
-		}
+static
+int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
+				struct cdrom_multisession *ms_info)
+{
+	int stat;
+	struct atapi_toc *toc;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-		lena = len;
-		if (lena  < 0) lena = 0;
+	/* Make sure the TOC information is valid. */
+	stat = cdrom_read_toc (drive, NULL);
+	if (stat) return stat;
 
-		{
-			char buf[lena];
-			if (len > 0) {
-				pc.buflen = len;
-				pc.buffer = buf;
-			}
+	toc = info->toc;
+	ms_info->addr.lba = toc->last_session_lba;
+	ms_info->xa_flag = toc->xa_flag;
 
-			stat = cdrom_queue_packet_command (drive, &pc);
+	return 0;
+}
 
-			if (len > 0)
-				copy_to_user ((void *)arg, buf, len);
-		}
 
-		return stat;
-	}
-#endif
+static
+int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
+		       struct cdrom_mcn *mcn_info)
+{
+	int stat;
+	char mcnbuf[24];
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 
-	default:
-		return -EPERM;
-	}
+	stat = cdrom_read_subchannel (drive, 2, /* get MCN */
+				      mcnbuf, sizeof (mcnbuf),
+				      NULL);
+	if (stat) return stat;
+
+	memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
+		sizeof (mcn_info->medium_catalog_number)-1);
+	mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
+		= '\0';
 
+	return 0;
 }
 
 
-
+
 /****************************************************************************
  * Other driver requests (open, close, check media change).
  */
 
-int ide_cdrom_check_media_change (ide_drive_t *drive)
+static
+int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
+				       int slot_nr)
 {
-	int retval;
-
-	(void) cdrom_check_status (drive, NULL);
-
-	retval = CDROM_STATE_FLAGS (drive)->media_changed;
-	CDROM_STATE_FLAGS (drive)->media_changed = 0;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
 
-	return retval;
-}
+	int retval;
 
+	if (slot_nr == CDSL_CURRENT) {
+		(void) cdrom_check_status (drive, NULL);
+		retval = CDROM_STATE_FLAGS (drive)->media_changed;
+		CDROM_STATE_FLAGS (drive)->media_changed = 0;
+	}
 
-int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
-{
-  /* no write access */
-	if (fp->f_mode & 2) {
-		--drive->usage;
-		return -EROFS;
+#if ! STANDARD_ATAPI
+	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+		retval = 0;
 	}
+#endif /* not STANDARD_ATAPI */
 
-	MOD_INC_USE_COUNT;
+	else {
+		struct atapi_changer_info *ci;
+		int stat = cdrom_read_changer_info (drive);
+		if (stat < 0)
+			return stat;
+		ci = info->changer_info;
 
-	/* If this is the first open, check the drive status. */
-	if (drive->usage == 1) {
-		int stat;
-		struct atapi_request_sense my_reqbuf;
-		my_reqbuf.sense_key = 0;
+		/* This test may be redundant with cdrom.c. */
+		if (slot_nr < 0 || slot_nr >= ci->hdr.nslots)
+			return -EINVAL;
 
-		/* Get the drive status. */
-		stat = cdrom_check_status (drive, &my_reqbuf);
+		retval = ci->slots[slot_nr].change;
+	}
 
-		/* If the tray is open, try to close it. */
-		if (stat && my_reqbuf.sense_key == NOT_READY) {
-			cdrom_eject (drive, 1, &my_reqbuf);
-			stat = cdrom_check_status (drive, &my_reqbuf);
-		}
+	return retval;
+}
 
-		/* If things worked ok, lock the door and read the
-		   TOC information. */
-		if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) {
-			(void) cdrom_lockdoor (drive, 1, &my_reqbuf);
-			(void) cdrom_read_toc (drive, &my_reqbuf);
-		}
-	}
 
+static
+int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
+{
 	return 0;
 }
 
@@ -2734,32 +2547,116 @@
  * Close down the device.  Invalidate all cached blocks.
  */
 
-void ide_cdrom_release (struct inode *inode, struct file *file,
-			ide_drive_t *drive)
+static
+void ide_cdrom_release_real (struct cdrom_device_info *cdi)
 {
-	if (drive->usage == 0) {
-		invalidate_buffers (inode->i_rdev);
-
-		/* Unlock the door. */
-		(void) cdrom_lockdoor (drive, 0, NULL);
-
-		/* Do an eject if we were requested to do so. */
-		if (CDROM_STATE_FLAGS (drive)->eject_on_close)
-			(void) cdrom_eject (drive, 0, NULL);
-	}
-	MOD_DEC_USE_COUNT;
 }
 
 
-
+
 /****************************************************************************
  * Device initialization.
  */
+static
+struct cdrom_device_ops ide_cdrom_dops = {
+	ide_cdrom_open_real,    /* open */
+	ide_cdrom_release_real, /* release */
+	ide_cdrom_drive_status, /* drive_status */
+	0, /* disc_status */
+	ide_cdrom_check_media_change_real, /* media_changed */
+	ide_cdrom_tray_move,    /* tray_move */
+	ide_cdrom_lock_door,    /* lock_door */
+	0, /* select_speed */
+	ide_cdrom_select_disc, /* select_disc */
+	ide_cdrom_get_last_session, /* get_last_session */
+	ide_cdrom_get_mcn, /* get_mcn */
+	ide_cdrom_reset, /* reset */
+	ide_cdrom_audio_ioctl, /* audio_ioctl */
+	ide_cdrom_dev_ioctl,   /* dev_ioctl */
+	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK
+	| CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
+	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO,  /* capability */
+	0 /* n_minors */
+};
+
+
+static int ide_cdrom_register (ide_drive_t *drive, int nslots)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *devinfo = &info->devinfo;
+	int minor = (drive->select.b.unit)<<PARTN_BITS;
+
+	devinfo->dev = MKDEV (HWIF(drive)->major, minor);
+	devinfo->ops = &ide_cdrom_dops;
+	devinfo->mask = 0;
+	*(int *)&devinfo->speed = 0;
+	*(int *)&devinfo->capacity = nslots;
+	devinfo->handle = (void *) drive;
+
+	return register_cdrom (devinfo, drive->name);
+}
+
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+	int stat, nslots;
+ 	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
+
+	nslots = 0;
+
+	if (CDROM_CONFIG_FLAGS (drive)->nec260)
+		return nslots;
+
+	stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
+				 (char *)&buf, sizeof (buf), NULL);
+	if (stat)
+		return nslots;
+
+	if (buf.cap.lock == 0)
+		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+
+#if ! STANDARD_ATAPI
+	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+		nslots = 3;
+	}
+
+	else
+#endif /* not STANDARD_ATAPI */
+	if (buf.cap.mechtype == mechtype_individual_changer ||
+	    buf.cap.mechtype == mechtype_cartridge_changer) {
+		struct atapi_mechstat_header mechbuf;
+
+		stat = cdrom_read_mech_status (drive, (char*)&mechbuf,
+					       sizeof (mechbuf), NULL);
+		if (!stat) {
+			CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+			CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1;
+			nslots = mechbuf.nslots;
+		}
+	}
+
+	if (CDROM_CONFIG_FLAGS (drive)->is_changer)
+		printk (" %s: ATAPI CDROM changer with %d slots\n", 
+			drive->name, nslots);
+
+	return nslots;
+}
+
 
 void ide_cdrom_setup (ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
-		
+	int nslots;
+
+	kdev_t dev = MKDEV (HWIF (drive)->major,
+			    drive->select.b.unit << PARTN_BITS);
+
+	set_device_ro (dev, 1);
 	blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] =
 		CD_FRAMESIZE;
 
@@ -2770,27 +2667,27 @@
 	CDROM_STATE_FLAGS (drive)->toc_valid     = 0;
 	CDROM_STATE_FLAGS (drive)->door_locked   = 0;
 
-	/* Turn this off by default, since many people don't like it. */
-	CDROM_STATE_FLAGS (drive)->eject_on_close= 0;
-
 #if NO_DOOR_LOCKING
 	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
 #else
 	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
 #endif
 
-	/* by default Sanyo 3 CD changer support is turned off and
-           ATAPI Rev 2.2+ standard support for CD changers is used */
-	CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
-
 	if (drive->id != NULL)
 		CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
 			((drive->id->config & 0x0060) == 0x20);
 	else
 		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
 
+	CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
+	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
+
 #if ! STANDARD_ATAPI
-	CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
+	/* by default Sanyo 3 CD changer support is turned off and
+           ATAPI Rev 2.2+ standard support for CD changers is used */
+	CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
+
+	CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
 	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0;
@@ -2818,10 +2715,13 @@
 		else if (strcmp (drive->id->model,
 				 "NEC CD-ROM DRIVE:260") == 0 &&
 			 strcmp (drive->id->fw_rev, "1.01") == 0) {
-			/* Old NEC260 (not R). */
+			/* Old NEC260 (not R).
+			   This drive was released before the 1.2 version
+			   of the spec. */
 			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->nec260         = 1;
 		}
 
 		else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
@@ -2832,7 +2732,7 @@
 		}
 
 		/* Sanyo 3 CD changer uses a non-standard command 
-                   for CD changing */
+                   for CD changing. */
                 else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
                          (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0)) {
 			/* uses CD in slot 0 when value is set to 3 */
@@ -2846,8 +2746,42 @@
 	info->sector_buffer     = NULL;
 	info->sector_buffered   = 0;
 	info->nsectors_buffered = 0;
+	info->changer_info      = NULL;
+
+	nslots = ide_cdrom_probe_capabilities (drive);
+
+	if (ide_cdrom_register (drive, nslots))
+		printk ("%s: Can't register\n", drive->name);
+}
+
+/* Forwarding functions to generic routines. */
+
+int ide_cdrom_ioctl (ide_drive_t *drive,
+		     struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	return cdrom_fops.ioctl (inode, file, cmd, arg);
+}
+
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+	return cdrom_fops.open (ip, fp);
+}
+
+void ide_cdrom_release (struct inode *inode, struct file *file,
+			ide_drive_t *drive)
+{
+	cdrom_fops.release (inode, file);
+}
+
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+	return cdrom_fops.check_media_change
+		(MKDEV (HWIF (drive)->major,
+			(drive->select.b.unit)<<PARTN_BITS));
 }
 
+
 int ide_cdrom_cleanup(ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
@@ -2876,7 +2810,8 @@
 	1,				/* supports_dma */
 	ide_cdrom_cleanup,		/* cleanup */
 	ide_do_rw_cdrom,		/* do_request */
-	NULL,				/* ??? or perhaps cdrom_end_request? */
+	NULL,				/* ??? or perhaps
+	cdrom_end_request? */
 	ide_cdrom_ioctl,		/* ioctl */
 	ide_cdrom_open,			/* open */
 	ide_cdrom_release,		/* release */
@@ -2886,6 +2821,27 @@
 	NULL				/* special */
 };
 
+
+#ifdef MODULE
+int init_module (void)
+{
+	return ide_cdrom_init();
+}
+
+void cleanup_module(void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+
+	while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
+		if (ide_cdrom_cleanup (drive)) {
+			printk ("%s: cleanup_module() called while still busy\n", drive->name);
+			failed++;
+		}
+	ide_unregister_module (&ide_cdrom_module);
+}
+#endif /* MODULE */
+ 
 int ide_cdrom_init (void)
 {
 	ide_drive_t *drive;
@@ -2913,39 +2869,6 @@
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
-
-#ifdef MODULE
-int init_module (void)
-{
-	return ide_cdrom_init();
-}
-
-void cleanup_module(void)
-{
-	ide_drive_t *drive;
-	int failed = 0;
-
-	while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
-		if (ide_cdrom_cleanup (drive)) {
-			printk ("%s: cleanup_module() called while still busy\n", drive->name);
-			failed++;
-		}
-	ide_unregister_module (&ide_cdrom_module);
-}
-#endif /* MODULE */
-
-
-/*
- * TODO (for 2.1?):
- *  Avoid printing error messages for expected errors from the drive.
- *  Integrate with generic cdrom driver.
- *  Query the drive to find what features are available
- *   before trying to use them.
- *  Integrate spindown time adjustment patch.
- *  CDROMRESET ioctl.
- *  Better support for changers.
- */
-
 
 
 /*==========================================================================*/

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