diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/MAINTAINERS linux/MAINTAINERS
--- /tmp/linux-2.2.12/MAINTAINERS	Sat Aug 28 14:27:57 1999
+++ linux/MAINTAINERS	Sat Aug 28 14:41:46 1999
@@ -398,6 +398,7 @@
 P:	Jens Axboe
 M:	axboe@image.dk
 L:	linux-kernel@vger.rutgers.edu
+W:	http://www.kernel.dk
 S:	Maintained
 
 IDE/ATAPI TAPE/FLOPPY DRIVERS
@@ -818,6 +819,7 @@
 P:	Jens Axboe
 M:	axboe@image.dk
 L:	linux-kernel@vger.rutgers.edu
+W:	http://www.kernel.dk
 S:	Maintained
 
 USB HUB AND UHCI DRIVERS
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- /tmp/linux-2.2.12/drivers/block/ide-cd.c	Mon Aug 23 13:32:23 1999
+++ linux/drivers/block/ide-cd.c	Thu Sep  2 22:51:44 1999
@@ -2,7 +2,7 @@
  * linux/drivers/block/ide-cd.c
  * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
  * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
- * Copyright (C) 1998, 1999 Jens Axboe
+ * Copyright (C) 1998, 1999 Jens Axboe <axboe@image.dk>
  *
  * May be copied or modified under the terms of the GNU General Public
  * License.  See linux/COPYING for more information.
@@ -12,11 +12,13 @@
  *
  * Suggestions are welcome. Patches that work are more welcome though. ;-)
  * For those wishing to work on this driver, please be sure you download
- * and comply with the latest ATAPI standard. This document can be
- * obtained by anonymous ftp from:
- * ftp://fission.dt.wdc.com/pub/standards/SFF_atapi/spec/SFF8020-r2.6/PS/8020r26.ps
+ * and comply with the latest Mt. Fuji (SFF8090 version 3) and ATAPI 
+ * (SFF-8020i rev 2.6) standards. These documents can be obtained by 
+ * anonymous ftp from:
+ * ftp://fission.dt.wdc.com/pub/standards/SFF/specs/INF-8020.PDF
+ * ftp://fission.dt.wdc.com/pub/standards/SFF/specs/INF-8090.PDF
  *
- * Drives that deviate from the ATAPI standard will be accomodated as much
+ * Drives that deviate from these standards will be accomodated as much
  * as possible via compile time or command-line options.  Since I only have
  * a few drives, you generally need to send me patches...
  *
@@ -243,12 +245,40 @@
  *                         Useful when using ide-cd in conjunction with
  *                         ide-scsi. TODO: non-modular way of doing the
  *                         same.
- *                         
+ *
+ * 4.54  Aug 5, 1999	- Support for MMC2 class commands through the generic
+ *			  packet interface to cdrom.c.
+ *			- Unified audio ioctl support, most of it.
+ *			- cleaned up various deprecated verify_area().
+ *			- Added ide_cdrom_packet() as the interface for
+ *			  the Uniform generic_packet().
+ *			- bunch of other stuff, will fill in logs later.
+ *			- report 1 slot for non-changers, like the other
+ *			  cd-rom drivers. don't report select disc for
+ *			  non-changers as well.
+ *			- mask out audio playing, if the device can't do it.
+ *
+ * 4.55  Sep 1, 1999	- Eliminated the rest of the audio ioctls, except
+ *			  for CDROMREADTOC[ENTRY|HEADER]. Some of the drivers
+ *			  use this independently of the actual audio handling.
+ *			  They will disappear later when I get the time to
+ *			  do it cleanly.
+ *			- Minimize the TOC reading - only do it when we
+ *			  know a media change has occured.
+ *			- Moved all the CDROMREADx ioctls to the Uniform layer.
+ *			- Heiko Eissfeldt <heiko@colossus.escape.de> supplied
+ *			  some fixes for CDI.
+ *			- CD-ROM leaving door locked fix from Andries
+ *			  Brouwer <Andries.Brouwer@cwi.nl>
+ *			- Erik Andersen <andersen@xmission.com> unified
+ *			  commands across the various drivers and how
+ *			  sense errors are handled.
  *
  *************************************************************************/
+ 
+#define IDECD_VERSION "4.55"
 
-#define IDECD_VERSION "4.53"
-
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -258,6 +288,7 @@
 #include <linux/interrupt.h>
 #include <linux/errno.h>
 #include <linux/cdrom.h>
+
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
@@ -302,12 +333,12 @@
 		   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)
+		    failed_command->c[0] == GPCMD_READ_SUBCHANNEL)
 			return;
 	}
 	if (reqbuf->error_code == 0x70 && reqbuf->sense_key  == 0x02
-	 && ((reqbuf->asc        == 0x3a && reqbuf->ascq       == 0x00) ||
-	     (reqbuf->asc        == 0x04 && reqbuf->ascq       == 0x01)))
+	 && ((reqbuf->asc      == 0x3a && reqbuf->ascq       == 0x00) ||
+	     (reqbuf->asc      == 0x04 && reqbuf->ascq       == 0x01)))
 	{
 		/*
 		 * Suppress the following errors:
@@ -344,15 +375,16 @@
 			s = buf;
 		} else {
 			int lo=0, mid, hi=ARY_LEN (sense_data_texts);
-			unsigned short key = (reqbuf->asc << 8);
+			unsigned long key = (reqbuf->sense_key << 16);
+			key |= (reqbuf->asc << 8);
 			if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
 				key |= reqbuf->ascq;
-
 			s = NULL;
 
 			while (hi > lo) {
 				mid = (lo + hi) / 2;
-				if (sense_data_texts[mid].asc_ascq == key) {
+				if (sense_data_texts[mid].asc_ascq == key ||
+				    sense_data_texts[mid].asc_ascq == (0xff0000|key)) {
 					s = sense_data_texts[mid].text;
 					break;
 				}
@@ -473,7 +505,7 @@
 	len = sizeof (*reqbuf) / 4;
 	len *= 4;
 
-	pc->c[0] = REQUEST_SENSE;
+	pc->c[0] = GPCMD_REQUEST_SENSE;
 	pc->c[4] = (unsigned char) len;
 	pc->buffer = (char *)reqbuf;
 	pc->buflen = len;
@@ -515,8 +547,8 @@
 				int *stat_ret)
 {
 	struct request *rq = HWGROUP(drive)->rq;
-	int stat, err, sense_key, cmd;
-
+	int stat, cmd, err, sense_key;
+	
 	/* Check for errors. */
 	stat = GET_STAT();
 	*stat_ret = stat;
@@ -524,8 +556,8 @@
 	if (OK_STAT (stat, good_stat, BAD_R_STAT))
 		return 0;
 
-	/* Got an error. */
-	err = IN_BYTE (IDE_ERROR_REG);
+	/* Get the IDE error register. */
+	err = GET_ERR();
 	sense_key = err >> 4;
 
 	if (rq == NULL)
@@ -563,7 +595,7 @@
 				   because workman constantly polls the drive
 				   with this command, and we don't want
 				   to uselessly fill up the syslog. */
-				if (pc->c[0] != SCMD_READ_SUBCHANNEL)
+				if (pc->c[0] != GPCMD_READ_SUBCHANNEL)
 					printk ("%s: tray open or drive not ready\n", drive->name);
 #endif
 			} else if (sense_key == UNIT_ATTENTION) {
@@ -1058,7 +1090,7 @@
 
 	/* Set up the command */
 	memset (&pc.c, 0, sizeof (pc.c));
-	pc.c[0] = READ_10;
+	pc.c[0] = GPCMD_READ_10;
 	pc.c[7] = (nframes >> 8);
 	pc.c[8] = (nframes & 0xff);
 	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
@@ -1103,7 +1135,7 @@
 	frame = sector / SECTORS_PER_FRAME;
 
 	memset (&pc.c, 0, sizeof (pc.c));
-	pc.c[0] = SEEK;
+	pc.c[0] = GPCMD_SEEK;
 	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
 	(void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr);
 }
@@ -1167,8 +1199,6 @@
 cdrom_lockdoor (ide_drive_t *drive, int lockflag,
 		struct atapi_request_sense *reqbuf);
 
-
-
 /* Interrupt routine for packet command completion. */
 static void cdrom_pc_intr (ide_drive_t *drive)
 {
@@ -1189,7 +1219,7 @@
 	if ((stat & DRQ_STAT) == 0) {
 		/* Some of the trailing request sense fields are optional, and
 		   some drives don't send them.  Sigh. */
-		if (pc->c[0] == REQUEST_SENSE &&
+		if (pc->c[0] == GPCMD_REQUEST_SENSE &&
 		    pc->buflen > 0 &&
 		    pc->buflen <= 5) {
 			while (pc->buflen > 0) {
@@ -1216,20 +1246,10 @@
 
 	/* Figure out how much data to transfer. */
 	thislen = pc->buflen;
-	if (thislen < 0) thislen = -thislen;
 	if (thislen > len) thislen = len;
 
 	/* The drive wants to be written to. */
 	if ((ireason & 3) == 0) {
-		/* Check that we want to write. */
-		if (pc->buflen > 0) {
-			printk ("%s: cdrom_pc_intr: Drive wants "
-				"to transfer data the wrong way!\n",
-				drive->name);
-			pc->stat = 1;
-			thislen = 0;
-		}
-
 		/* Transfer the data. */
 		atapi_output_bytes (drive, pc->buffer, thislen);
 
@@ -1243,19 +1263,11 @@
 
 		/* Keep count of how much data we've moved. */
 		pc->buffer += thislen;
-		pc->buflen += thislen;
+		pc->buflen -= thislen;
 	}
 
 	/* Same drill for reading. */
 	else if ((ireason & 3) == 2) {
-		/* Check that we want to read. */
-		if (pc->buflen < 0) {
-			printk ("%s: cdrom_pc_intr: Drive wants to "
-				"transfer data the wrong way!\n",
-				drive->name);
-			pc->stat = 1;
-			thislen = 0;
-		}
 
 		/* Transfer the data. */
 		atapi_input_bytes (drive, pc->buffer, thislen);
@@ -1302,11 +1314,8 @@
 	struct cdrom_info *info = drive->driver_data;
 
 	info->dma = 0;
-
-	len = pc->buflen;
-	if (len < 0) len = -len;
-
 	pc->stat = 0;
+	len = pc->buflen;
 
 	/* Start sending the command to the drive. */
 	cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
@@ -1359,10 +1368,10 @@
 				   a disk.  Retry, but wait a little to give
 				   the drive time to complete the load. */
 				cdrom_sleep (HZ);
-			} else
+			} else {
 				/* Otherwise, don't retry. */
 				retries = 0;
-
+			}
 			--retries;
 		}
 
@@ -1379,17 +1388,19 @@
 		   and we think that the door is presently, lock it again.
 		   (The door was probably unlocked via an explicit
 		   CDROMEJECT ioctl.) */
-		if (CDROM_STATE_FLAGS (drive)->door_locked == 0 && drive->usage &&
-		    (pc->c[0] != REQUEST_SENSE &&
-		     pc->c[0] != ALLOW_MEDIUM_REMOVAL &&
-		     pc->c[0] != START_STOP)) {
+		if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
+		    (pc->c[0] != GPCMD_TEST_UNIT_READY &&
+		     pc->c[0] != GPCMD_REQUEST_SENSE &&
+		     pc->c[0] != GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL &&
+		     pc->c[0] != GPCMD_START_STOP_UNIT &&
+		     pc->c[0] != GPCMD_MODE_SENSE_10 &&
+		     pc->c[0] != GPCMD_MODE_SELECT_10)) {
 			(void) cdrom_lockdoor (drive, 1, NULL);
 		}
 		return 0;
 	}
 }
 
-
 /****************************************************************************
  * cdrom driver request routine.
  */
@@ -1494,7 +1505,7 @@
 	memset (&pc, 0, sizeof (pc));
 
 	pc.sense_data = reqbuf;
-	pc.c[0] = TEST_UNIT_READY;
+	pc.c[0] = GPCMD_TEST_UNIT_READY;
 
 #if ! STANDARD_ATAPI
         /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
@@ -1515,7 +1526,7 @@
 	struct atapi_request_sense my_reqbuf;
 	int stat;
 	struct packet_command pc;
-
+	
 	if (reqbuf == NULL)
 		reqbuf = &my_reqbuf;
 
@@ -1526,7 +1537,7 @@
 		memset (&pc, 0, sizeof (pc));
 		pc.sense_data = reqbuf;
 
-		pc.c[0] = ALLOW_MEDIUM_REMOVAL;
+		pc.c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
 		pc.c[4] = (lockflag != 0);
 		stat = cdrom_queue_packet_command (drive, &pc);
 	}
@@ -1561,45 +1572,18 @@
 {
 	struct packet_command pc;
 
-	if (CDROM_CONFIG_FLAGS (drive)->no_eject==1 && ejectflag==0)
+	if (CDROM_CONFIG_FLAGS (drive)->no_eject && !ejectflag)
 		return -EDRIVE_CANT_DO_THIS;
+	
+	/* reload fails on some drives, if the tray is locked */
+	if (CDROM_STATE_FLAGS (drive)->door_locked && ejectflag)
+		return 0;
 
 	memset (&pc, 0, sizeof (pc));
 	pc.sense_data = reqbuf;
 
-	pc.c[0] = START_STOP;
-	pc.c[4] = 2 + (ejectflag != 0);
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-
-static int
-cdrom_pause (ide_drive_t *drive, int pauseflag,
-	     struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.c[0] = SCMD_PAUSE_RESUME;
-	pc.c[8] = !pauseflag;
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-
-static int
-cdrom_startstop (ide_drive_t *drive, int startflag,
-		 struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.c[0] = START_STOP;
-	pc.c[1] = 1;
-	pc.c[4] = startflag;
+	pc.c[0] = GPCMD_START_STOP_UNIT;
+	pc.c[4] = 0x02 + (ejectflag != 0);
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
@@ -1608,8 +1592,8 @@
 		     struct atapi_request_sense *reqbuf)
 {
 	struct {
-		unsigned lba;
-		unsigned blocklen;
+		__u32 lba;
+		__u32 blocklen;
 	} capbuf;
 
 	int stat;
@@ -1618,7 +1602,7 @@
 	memset (&pc, 0, sizeof (pc));
 	pc.sense_data = reqbuf;
 
-	pc.c[0] = READ_CAPACITY;
+	pc.c[0] = GPCMD_READ_CDVD_CAPACITY;
 	pc.buffer = (char *)&capbuf;
 	pc.buflen = sizeof (capbuf);
 
@@ -1642,7 +1626,7 @@
 
 	pc.buffer =  buf;
 	pc.buflen = buflen;
-	pc.c[0] = SCMD_READ_TOC;
+	pc.c[0] = GPCMD_READ_TOC_PMA_ATIP;
 	pc.c[6] = trackno;
 	pc.c[7] = (buflen >> 8);
 	pc.c[8] = (buflen & 0xff);
@@ -1670,11 +1654,10 @@
 		toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
 						    GFP_KERNEL);
 		info->toc = toc;
-	}
-
-	if (toc == NULL) {
-		printk ("%s: No cdrom TOC buffer!\n", drive->name);
-		return -EIO;
+		if (toc == NULL) {
+			printk ("%s: No cdrom TOC buffer!\n", drive->name);
+			return -ENOMEM;
+		}
 	}
 
 	/* Check to see if the existing data is still valid.
@@ -1686,9 +1669,7 @@
 
 	/* First read just the header, so we know how long the TOC is. */
 	stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
-				    sizeof (struct atapi_toc_header) +
-				    sizeof (struct atapi_toc_entry),
-				    reqbuf);
+				    sizeof (struct atapi_toc_header), reqbuf);
 	if (stat) return stat;
 
 #if ! STANDARD_ATAPI
@@ -1703,12 +1684,46 @@
 	if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
 
 	/* Now read the whole schmeer. */
-	stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
+	stat = cdrom_read_tocentry (drive, toc->hdr.first_track-1, 1, 0,
+				   (char *)&toc->hdr,
 				    sizeof (struct atapi_toc_header) +
 				    (ntracks+1) *
-				      sizeof (struct atapi_toc_entry),
-				    reqbuf);
-	if (stat) return stat;
+				    sizeof (struct atapi_toc_entry), reqbuf);
+
+	if (stat && toc->hdr.first_track > 1) {
+		/* Cds with CDI tracks only don't have any TOC entries,
+		   despite of this the returned values are
+		   first_track == last_track = number of CDI tracks + 1,
+		   so that this case is indistinguishable from the same
+		   layout plus an additional audio track.
+		   If we get an error for the regular case, we assume
+		   a CDI without additional audio tracks. In this case
+		   the readable TOC is empty (CDI tracks are not included)
+		   and only holds the Leadout entry. Heiko Eißfeldt */
+		ntracks = 0;
+		stat = cdrom_read_tocentry (drive, CDROM_LEADOUT, 1,
+					    0, (char *)&toc->hdr,
+					    sizeof (struct atapi_toc_header) +
+					   (ntracks+1) *
+					    sizeof (struct atapi_toc_entry),
+					    reqbuf);
+		if (stat) {
+			return stat;
+		}
+#if ! STANDARD_ATAPI
+		if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+			toc->hdr.first_track = bin2bcd(CDROM_LEADOUT);
+			toc->hdr.last_track = bin2bcd(CDROM_LEADOUT);
+		} else
+#endif  /* not STANDARD_ATAPI */
+		{
+			toc->hdr.first_track = CDROM_LEADOUT;
+			toc->hdr.last_track = CDROM_LEADOUT;
+		}
+	} else if (stat) {
+		return stat;
+	}
+
 	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
 
 #if ! STANDARD_ATAPI
@@ -1732,10 +1747,18 @@
 	}
 
 	/* Read the multisession information. */
-	stat = cdrom_read_tocentry (drive, 0, 1, 1,
-				    (char *)&ms_tmp, sizeof (ms_tmp),
-				    reqbuf);
-	if (stat) return stat;
+	if (toc->hdr.first_track != CDROM_LEADOUT) {
+		/* Read the multisession information. */
+		stat = cdrom_read_tocentry (drive, toc->hdr.first_track-1, 1, 1,
+					   (char *)&ms_tmp, sizeof (ms_tmp),
+					    reqbuf);
+		if (stat) return stat;
+	} else {
+		ms_tmp.ent.addr.msf.minute = 0;
+		ms_tmp.ent.addr.msf.second = 2;
+		ms_tmp.ent.addr.msf.frame  = 0;
+		ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT;
+	}
 
 #if ! STANDARD_ATAPI
 	if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
@@ -1749,6 +1772,12 @@
 	toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
 
 	/* Now try to get the total cdrom capacity. */
+#if 0
+	stat = cdrom_get_last_written(MKDEV(HWIF(drive)->major,
+				      drive->select.b.unit << PARTN_BITS),
+				     (long *)&toc->capacity);
+	if (stat)
+#endif
 	stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
 	if (stat) toc->capacity = 0x1fffff;
 
@@ -1775,7 +1804,7 @@
 
 	pc.buffer =  buf;
 	pc.buflen = buflen;
-	pc.c[0] = SCMD_READ_SUBCHANNEL;
+	pc.c[0] = GPCMD_READ_SUBCHANNEL;
 	pc.c[1] = 2;     /* MSF addressing */
 	pc.c[2] = 0x40;  /* request subQ data */
 	pc.c[3] = format;
@@ -1798,7 +1827,7 @@
 
 	pc.buffer =  buf;
 	pc.buflen = buflen;
-	pc.c[0] = MODE_SENSE_10;
+	pc.c[0] = GPCMD_MODE_SENSE_10;
 	pc.c[2] = pageno | (modeflag << 6);
 	pc.c[7] = (buflen >> 8);
 	pc.c[8] = (buflen & 0xff);
@@ -1815,8 +1844,8 @@
 	pc.sense_data = reqbuf;
 
 	pc.buffer =  buf;
-	pc.buflen = - buflen;
-	pc.c[0] = MODE_SELECT_10;
+	pc.buflen = buflen;
+	pc.c[0] = GPCMD_MODE_SELECT_10;
 	pc.c[1] = 0x10;
 	pc.c[2] = pageno;
 	pc.c[7] = (buflen >> 8);
@@ -1839,7 +1868,7 @@
 	else
 	    speed *= 177;   /* Nx to kbytes/s */
 
-	pc.c[0] = SET_CD_SPEED;
+	pc.c[0] = GPCMD_SET_SPEED;
 	/* Read Drive speed in kbytes/second MSB */
 	pc.c[2] = (speed >> 8) & 0xff;	
 	/* Read Drive speed in kbytes/second LSB */
@@ -1855,67 +1884,6 @@
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
-static int
-cdrom_play_lba_range_1 (ide_drive_t *drive, int lba_start, int lba_end,
-			    struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.c[0] = SCMD_PLAYAUDIO_MSF;
-	lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
-	lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
-
-#if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd) {
-		pc.c[3] = bin2bcd (pc.c[3]);
-		pc.c[4] = bin2bcd (pc.c[4]);
-		pc.c[5] = bin2bcd (pc.c[5]);
-		pc.c[6] = bin2bcd (pc.c[6]);
-		pc.c[7] = bin2bcd (pc.c[7]);
-		pc.c[8] = bin2bcd (pc.c[8]);
-	}
-#endif /* not STANDARD_ATAPI */
-
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-
-/* Play audio starting at LBA LBA_START and finishing with the
-   LBA before LBA_END. */
-static int
-cdrom_play_lba_range (ide_drive_t *drive, int lba_start, int lba_end,
-		      struct atapi_request_sense *reqbuf)
-{
-	int i, stat;
-	struct atapi_request_sense my_reqbuf;
-
-	if (reqbuf == NULL)
-		reqbuf = &my_reqbuf;
-
-	/* Some drives, will, for certain audio cds,
-	   give an error if you ask them to play the entire cd using the
-	   values which are returned in the TOC.  The play will succeed,
-	   however, if the ending address is adjusted downwards
-	   by a few frames. */
-	for (i=0; i<75; i++) {
-		stat = cdrom_play_lba_range_1 (drive, lba_start, lba_end,
-					       reqbuf);
-
-		if (stat == 0 ||
-		    !(reqbuf->sense_key == ILLEGAL_REQUEST &&
-		      reqbuf->asc == 0x24))
-			return stat;
-
-		--lba_end;
-		if (lba_end <= lba_start) break;
-	}
-
-	return stat;
-}
-
 
 static
 int cdrom_get_toc_entry (ide_drive_t *drive, int track,
@@ -1923,17 +1891,12 @@
 			 struct atapi_request_sense *reqbuf)
 {
 	struct cdrom_info *info = drive->driver_data;
-	int stat, ntracks;
-	struct atapi_toc *toc;
-
-	/* Make sure our saved TOC is valid. */
-	stat = cdrom_read_toc (drive, reqbuf);
-	if (stat) return stat;
-
-	toc = info->toc;
+	struct atapi_toc *toc = info->toc;
+	int ntracks;
 
 	/* Check validity of requested track number. */
 	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (toc->hdr.first_track == CDROM_LEADOUT) ntracks = 0;
 	if (track == CDROM_LEADOUT)
 		*ent = &toc->ent[ntracks];
 	else if (track < toc->hdr.first_track ||
@@ -1946,44 +1909,6 @@
 }
 
 
-static int
-cdrom_read_block (ide_drive_t *drive, int format, int lba, int nblocks,
-		  char *buf, int buflen,
-		  struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-	struct atapi_request_sense my_reqbuf;
-
-	if (reqbuf == NULL)
-		reqbuf = &my_reqbuf;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.buffer = buf;
-	pc.buflen = buflen;
-
-#if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->nec260)
-		pc.c[0] = 0xd4;
-	else
-#endif  /* not STANDARD_ATAPI */
-		pc.c[0] = READ_CD;
-
-	pc.c[1] = (format << 2);
-	put_unaligned(htonl(lba), (unsigned int *) &pc.c[2]);
-	pc.c[8] = (nblocks & 0xff);
-	pc.c[7] = ((nblocks>>8) & 0xff);
-	pc.c[6] = ((nblocks>>16) & 0xff);
-	if (format <= 1)
-		pc.c[9] = 0xf8;         /* returns 2352 for any format */
-	else
-		pc.c[9] = 0x10;
-
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-
 /* If SLOT<0, unload the current slot.  Otherwise, try to load SLOT. */
 static int
 cdrom_load_unload (ide_drive_t *drive, int slot,
@@ -2018,7 +1943,7 @@
 		memset (&pc, 0, sizeof (pc));
 		pc.sense_data = reqbuf;
 
-		pc.c[0] = LOAD_UNLOAD;
+		pc.c[0] = GPCMD_LOAD_UNLOAD;
 		pc.c[4] = 2 + (slot >= 0);
 		pc.c[8] = slot;
 		return cdrom_queue_packet_command (drive, &pc);
@@ -2039,13 +1964,12 @@
 
 	pc.buffer = buf;
 	pc.buflen = buflen;
-	pc.c[0] = MECHANISM_STATUS;
+	pc.c[0] = GPCMD_MECHANISM_STATUS;
 	pc.c[8] = (buflen >> 8);
 	pc.c[9] = (buflen & 0xff);
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
-
 /* Read the drive mechanism status and slot table into our internal buffer.
    If the buffer does not yet exist, allocate it. */
 static int
@@ -2087,6 +2011,23 @@
 		 NULL);
 }
 
+/* the generic packet interface to cdrom.c */
+static int ide_cdrom_packet(struct cdrom_device_info *cdi,
+			    struct cdrom_generic_command *cgc)
+{
+	struct packet_command pc;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+	/* here we queue the commands from the uniform CD-ROM
+	   layer. the packet must be complete, as we do not
+	   touch it at all. */
+	memset(&pc, 0, sizeof(pc));
+	memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE);
+	pc.buffer = cgc->buffer;
+	pc.buflen = cgc->buflen;
+	cgc->stat = cdrom_queue_packet_command(drive, &pc);
+	return cgc->stat;
+}
 
 static
 int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
@@ -2094,146 +2035,23 @@
 			 
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_info *info = drive->driver_data;
-
 
 	switch (cmd) {
-	case CDROMREADRAW:
-	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 {	/* for RAW and MODE2. */
-			blocksize = CD_FRAMESIZE_RAW;
-			format = 0;
-		}
-		
-		copy_from_user_ret(&msf, (void *)arg, sizeof (msf), -EFAULT); 
-
-		lba = msf_to_lba(msf.cdmsf_min0, 
-				 msf.cdmsf_sec0, 
-				 msf.cdmsf_frame0);
-
-		/* Make sure the TOC is up to date. */
-		if (cmd != CDROMREADRAW) {
-			stat = cdrom_read_toc (drive, NULL);
-			if (stat) 
-				return stat;
-
-			toc = info->toc;
-
-			if (lba < 0 || lba >= toc->capacity)
-				return -EINVAL;
-		}
-
-		buf = (char *) kmalloc (blocksize, GFP_KERNEL);
-		if (buf == NULL)
-			return -ENOMEM;
-
-		stat = cdrom_read_block (drive, format, lba, 1, buf,
-					 blocksize, NULL);
-
-		if (stat == 0) {
-			if (cmd == CDROMREADMODE2) {
-				/* For Mode2, skip the Sync, Header, and Subheader */
-				copy_to_user_ret((char *)arg, buf+16, CD_FRAMESIZE_RAW0, -EFAULT);
-			} else {
-				copy_to_user_ret((char *)arg, buf, blocksize, -EFAULT);
-			}
-		}
-
-		kfree (buf);
-		return stat;
-	}
-
-	/* 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;
-
-		toc = info->toc;
-
-		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
-		if (stat) return stat;
-
-		copy_from_user (&ra, (void *)arg, sizeof (ra));
-
-		if (ra.nframes < 0 || ra.nframes > toc->capacity)
-			return -EINVAL;
-		else if (ra.nframes == 0)
-			return 0;
-
-		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;
-
-		if (lba < 0 || lba >= toc->capacity)
-			return -EINVAL;
-
-		buf = (char *) kmalloc (CDROM_NBLOCKS_BUFFER*CD_FRAMESIZE_RAW,
-					GFP_KERNEL);
-		if (buf == NULL)
-			return -ENOMEM;
-
-		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 (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 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));
+ 		if (copy_from_user(&spindown, (void *) arg, sizeof(char)))
+			return -EFAULT;
  
- 		stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
+ 		stat = cdrom_mode_sense (drive, GPMODE_CDROM_PAGE, 0, buffer,
  					 sizeof (buffer), NULL);
  		if (stat) return stat;
 
  		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
 
- 		return cdrom_mode_select (drive, PAGE_CDROM, buffer,
+ 		return cdrom_mode_select (drive, GPMODE_CDROM_PAGE, buffer,
  					  sizeof (buffer), NULL);			
  	} 
  
@@ -2242,79 +2060,24 @@
  		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,
+ 		stat = cdrom_mode_sense (drive, GPMODE_CDROM_PAGE, 0, buffer,
                                          sizeof (buffer), NULL);
  		if (stat) return stat;
  
  		spindown = buffer[11] & 0x0f;
  
- 		copy_to_user ((void *) arg, &spindown, sizeof (char));
+		if (copy_to_user((void *) arg, &spindown, sizeof (char)))
+			return -EFAULT;
  
  		return 0;
  	}
   
-#ifdef ALLOW_TEST_PACKETS
-	case 0x1234: {
-		int stat;
-		struct packet_command pc;
-		int len, lena;
-
-		memset (&pc, 0, sizeof (pc));
-
-		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 = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
-		if (stat) return stat;
-		copy_from_user (&len, (void *) arg , sizeof (len));
-		arg += sizeof (len);
-
-		lena = len;
-		if (lena  < 0) lena = -lena;
-
-		{
-			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;
-			}
-
-			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)
@@ -2324,47 +2087,6 @@
 	struct cdrom_info *info = drive->driver_data;
 
 	switch (cmd) {
-	case CDROMSUBCHNL: {
-		struct atapi_cdrom_subchnl scbuf;
-		int stat;
-		struct cdrom_subchnl *subchnl = (struct cdrom_subchnl *)arg;
-
-		stat = cdrom_read_subchannel (drive, 1, /* current position */
-					      (char *)&scbuf, sizeof (scbuf),
-					      NULL);
-		if (stat) return stat;
-
-#if ! STANDARD_ATAPI
-		if (CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd) {
-			msf_from_bcd (&scbuf.acdsc_absaddr.msf);
-			msf_from_bcd (&scbuf.acdsc_reladdr.msf);
-		}
-		if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
-			scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
-#endif /* not STANDARD_ATAPI */
-
-		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;
-
-		return 0;
-	}
-
 	case CDROMREADTOCHDR: {
 		int stat;
 		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
@@ -2392,119 +2114,22 @@
 
 		tocentry->cdte_ctrl = toce->control;
 		tocentry->cdte_adr  = toce->adr;
-		tocentry->cdte_format = CDROM_LBA;
-		tocentry->cdte_addr.lba = toce->addr.lba;
-
-		return 0;
-	}
-
-	case CDROMPLAYMSF: {
-		struct cdrom_msf *msf = (struct cdrom_msf *) arg;
-		int lba_start, lba_end;
-
-		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);
-	}
-
-	/* 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 = 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);
-		if (stat) return stat;
-
-		if (ti->cdti_trk1 != CDROM_LEADOUT) ++last_toc;
-		lba_start = first_toc->addr.lba;
-		lba_end   = last_toc->addr.lba;
-
-		if (lba_end <= lba_start) return -EINVAL;
-
-		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
-	}
-
-	case CDROMVOLCTRL: {
-		struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
-		char buffer[24], mask[24];
-		int stat;
-
-		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;
-
-		buffer[1] = buffer[2] = 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];
-
-		return cdrom_mode_select (drive, PAGE_AUDIO, buffer,
-					  sizeof (buffer), NULL);
-	}
-
-	case CDROMVOLREAD: {
-		struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
-		char buffer[24];
-		int stat;
-
-		stat = cdrom_mode_sense (drive, PAGE_AUDIO, 0, buffer,
-					 sizeof (buffer), NULL);
-		if (stat) return stat;
-
-		volctrl->channel0 = buffer[17];
-		volctrl->channel1 = buffer[19];
-		volctrl->channel2 = buffer[21];
-		volctrl->channel3 = buffer[23];
+		if (tocentry->cdte_format == CDROM_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;
 
 		return 0;
 	}
 
-	case CDROMSTART:
-		return cdrom_startstop (drive, 1, NULL);
-
-	case CDROMSTOP: {
-#ifdef IHAVEADOLPHIN
-               /*  Certain Drives require this.  Most don't
-                   and will produce errors upon CDROMSTOP
-                   pit says the Dolphin needs this.  If you
-                   own a dolphin, just define IHAVEADOLPHIN somewhere */
-                int stat;
-                stat = cdrom_startstop (drive, 0, NULL);
-                if (stat) return stat;
-                return cdrom_eject (drive, 1, NULL);
-#endif /* end of IHAVEADOLPHIN  */
-               return cdrom_startstop (drive, 0, NULL);
-	}
-
-	case CDROMPAUSE:
-		return cdrom_pause (drive, 1, NULL);
-
-	case CDROMRESUME:
-		return cdrom_pause (drive, 0, NULL);
-
-
 	default:
 		return -EINVAL;
 	}
 }
 
-
 static
 int ide_cdrom_reset (struct cdrom_device_info *cdi)
 {
@@ -2531,7 +2156,6 @@
 	return cdrom_eject (drive, !position, NULL);
 }
 
-
 static
 int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
 {
@@ -2557,7 +2181,7 @@
         do {    /* we seem to get stat=0x01,err=0x00 the first time (??) */
                 if (attempts-- <= 0)
                         return 0;
-                stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
+                stat = cdrom_mode_sense (drive, GPMODE_CAPABILITIES_PAGE, 0,
                                         (char *)&buf, sizeof (buf), NULL);
         } while (stat);
 
@@ -2650,12 +2274,6 @@
 		if (stat && my_reqbuf.sense_key == NOT_READY)
 			return -ENOENT;
 
-		if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) {
-			stat = cdrom_read_toc (drive, &my_reqbuf);
-			if (stat)
-				return stat;
-		}
-
 		if (was_locked)
 			(void) cdrom_lockdoor (drive, 1, NULL);
 
@@ -2710,15 +2328,10 @@
 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;
 
-	/* Make sure the TOC information is valid. */
-	stat = cdrom_read_toc (drive, NULL);
-	if (stat) return stat;
-
 	toc = info->toc;
 	ms_info->addr.lba = toc->last_session_lba;
 	ms_info->xa_flag = toc->xa_flag;
@@ -2726,7 +2339,6 @@
 	return 0;
 }
 
-
 static
 int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
 		       struct cdrom_mcn *mcn_info)
@@ -2760,9 +2372,9 @@
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	struct cdrom_info *info = drive->driver_data;
-
+	struct atapi_request_sense reqbuf;
 	int retval;
-
+	
 	if (slot_nr == CDSL_CURRENT) {
 		(void) cdrom_check_status (drive, NULL);
 		retval = CDROM_STATE_FLAGS (drive)->media_changed;
@@ -2788,7 +2400,17 @@
 
 		retval = ci->slots[slot_nr].change;
 	}
-
+	
+	/* if the media has changed, check if a disc is in the drive
+	   and read the toc info. */
+	if (retval || !CDROM_STATE_FLAGS (drive)->toc_valid) {
+		/* if cdrom_read_toc fails, return 1 to indicate
+		   that a disc change has occured. there might not
+		   be a disc in the drive. */
+		if ((retval = cdrom_read_toc (drive, &reqbuf)))
+			return 1;
+	}
+		
 	return retval;
 }
 
@@ -2832,9 +2454,11 @@
 	ide_cdrom_dev_ioctl,   /* dev_ioctl */
 	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED
 	| CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
-	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET 
-	| CDC_IOCTLS | CDC_DRIVE_STATUS,  /* capability */
-	0 /* n_minors */
+	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS
+	| CDC_DRIVE_STATUS | CDC_CD_R | CDC_CD_RW | CDC_DVD
+	| CDC_DVD_R| CDC_DVD_RAM | CDC_GENERIC_PACKET, /* capability */
+	0, /* n_minors */
+	ide_cdrom_packet
 };
 
 static int ide_cdrom_register (ide_drive_t *drive, int nslots)
@@ -2850,6 +2474,25 @@
 	*(int *)&devinfo->capacity = nslots;
 	devinfo->handle = (void *) drive;
 	strcpy(devinfo->name, drive->name);
+	
+	/* set capability mask to match the probe. */
+	if (!CDROM_CONFIG_FLAGS (drive)->cd_r)
+		devinfo->mask |= CDC_CD_R;
+	if (!CDROM_CONFIG_FLAGS (drive)->cd_rw)
+		devinfo->mask |= CDC_CD_RW;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd)
+		devinfo->mask |= CDC_DVD;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd_r)
+		devinfo->mask |= CDC_DVD_R;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd_ram)
+		devinfo->mask |= CDC_DVD_RAM;
+	if (!CDROM_CONFIG_FLAGS (drive)->is_changer)
+		devinfo->mask |= CDC_SELECT_DISC;
+	if (!CDROM_CONFIG_FLAGS (drive)->audio_play)
+		devinfo->mask |= CDC_PLAY_AUDIO;
+	if (!CDROM_CONFIG_FLAGS (drive)->close_tray)
+		devinfo->mask |= CDC_CLOSE_TRAY;
+		
 	return register_cdrom (devinfo);
 }
 
@@ -2857,7 +2500,7 @@
 static
 int ide_cdrom_probe_capabilities (ide_drive_t *drive)
 {
-	int stat, nslots = 0, attempts = 3;
+	int stat, nslots = 1, attempts = 3;
  	struct {
 		char pad[8];
 		struct atapi_capabilities_page cap;
@@ -2869,7 +2512,7 @@
 	do {	/* we seem to get stat=0x01,err=0x00 the first time (??) */
 		if (attempts-- <= 0)
 			return 0;
-		stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
+		stat = cdrom_mode_sense (drive, GPMODE_CAPABILITIES_PAGE, 0,
 				 	(char *)&buf, sizeof (buf), NULL);
 	} while (stat);
 
@@ -2888,7 +2531,11 @@
 	if (buf.cap.dvd_ram_write)
 		CDROM_CONFIG_FLAGS (drive)->dvd_r = 1;
 	if (buf.cap.dvd_r_write)
-		CDROM_CONFIG_FLAGS (drive)->dvd_rw = 1;
+		CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1;
+	if (buf.cap.audio_play)
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
+	if (buf.cap.mechtype == 0)
+		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
 
 #if ! STANDARD_ATAPI
 	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
@@ -2928,10 +2575,10 @@
         	drive->name, CDROM_CONFIG_FLAGS (drive)->max_speed,
 		(CDROM_CONFIG_FLAGS (drive)->dvd) ? "DVD-ROM" : "CD-ROM");
 
-	if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_rw)
+	if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram)
         	printk (" DVD%s%s", 
-        	(CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-RAM" : "", 
-        	(CDROM_CONFIG_FLAGS (drive)->dvd_rw)? "/RW" : "");
+        	(CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : "");
 
         if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) 
         	printk (" CD%s%s", 
@@ -2975,7 +2622,7 @@
 	drive->special.all = 0;
 	drive->ready_stat = 0;
 
-	CDROM_STATE_FLAGS (drive)->media_changed = 0;
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
 	CDROM_STATE_FLAGS (drive)->toc_valid     = 0;
 	CDROM_STATE_FLAGS (drive)->door_locked   = 0;
 
@@ -2997,9 +2644,11 @@
 	CDROM_CONFIG_FLAGS (drive)->test_write = 0;
 	CDROM_CONFIG_FLAGS (drive)->dvd = 0;
 	CDROM_CONFIG_FLAGS (drive)->dvd_r = 0;
-	CDROM_CONFIG_FLAGS (drive)->dvd_rw = 0;
+	CDROM_CONFIG_FLAGS (drive)->dvd_ram = 0;
 	CDROM_CONFIG_FLAGS (drive)->no_eject = 1;
 	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
+	CDROM_CONFIG_FLAGS (drive)->audio_play = 0;
+	CDROM_CONFIG_FLAGS (drive)->close_tray = 1;
 	
 	/* limit transfer size per interrupt. */
 	CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0;
@@ -3128,7 +2777,6 @@
 			(drive->select.b.unit)<<PARTN_BITS));
 }
 
-
 static
 int ide_cdrom_cleanup(ide_drive_t *drive)
 {
@@ -3244,11 +2892,3 @@
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
-
-
-/*==========================================================================*/
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- /tmp/linux-2.2.12/drivers/block/ide-cd.h	Mon Aug 23 13:32:23 1999
+++ linux/drivers/block/ide-cd.h	Sat Aug 28 14:15:51 1999
@@ -1,9 +1,9 @@
 #ifndef _IDE_CD_H
 #define _IDE_CD_H
 /*
- *  linux/drivers/block/ide_modes.h
+ *  linux/drivers/block/ide_cd.h
  *
- *  Copyright (C) 1996  Erik Andersen
+ *  Copyright (C) 1996, 1997, 1998  Erik Andersen
  *  Copyright (C) 1998, 1999 Jens Axboe
  */
 
@@ -55,94 +55,37 @@
 #define REQUEST_SENSE_COMMAND 4316
 #define RESET_DRIVE_COMMAND   4317
 
-/*
- * For controlling drive spindown time.
- */
-#define CDROMGETSPINDOWN        0x531d
-#define CDROMSETSPINDOWN        0x531e
- 
-
-/* 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 INQUIRY                 0x12
-#define START_STOP              0x1b
-#define ALLOW_MEDIUM_REMOVAL    0x1e
-#define READ_CAPACITY           0x25
-#define READ_10                 0x28
-#define SEEK			0x2b
-#define READ_HEADER             0x44
-#define STOP_PLAY_SCAN		0x4e
-#define MODE_SELECT_10          0x55
-#define MODE_SENSE_10           0x5a
-#define LOAD_UNLOAD             0xa6
-#define READ_12                 0xa8
-#define READ_CD_MSF             0xb9
-#define SCAN			0xba
-#define SET_CD_SPEED            0xbb
-#define PLAY_CD                 0xbc
-#define MECHANISM_STATUS        0xbd
-#define READ_CD                 0xbe
-
-/* DVD Opcodes */
-#define DVD_GET_PERFORMANCE	0xac
-
-
-/* Page codes for mode sense/set */
-
-#define PAGE_READERR            0x01
-#define PAGE_CDROM              0x0d
-#define PAGE_AUDIO              0x0e
-#define PAGE_CAPABILITIES       0x2a
-#define PAGE_ALL                0x3f
-
-
-/* ATAPI sense keys (from table 140 of ATAPI 2.6) */
-
-#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 CDROM drives.
-   To save space in the ide_drive_t struct, use some fields which
-   doesn't make sense for CDROMs -- `bios_cyl' 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. */
-	__u8 no_eject         : 1; /* Drive cannot eject the disc. */
-	__u8 nec260           : 1; /* Drive is a pre-1.2 NEC 260 drive. */
-	__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. */
-	__u8 is_changer       : 1; /* Drive is a changer. */
-	__u8 cd_r             : 1; /* Drive can write to CD-R media . */
-	__u8 cd_rw            : 1; /* Drive can write to CD-R/W media . */
-	__u8 dvd              : 1; /* Drive is a DVD-ROM */
-	__u8 dvd_r            : 1; /* Drive can write DVD-RAM */
-	__u8 dvd_rw           : 1; /* Drive can write DVD-R/W */
-	__u8 test_write       : 1; /* Drive can fake writes */
-	__u8 supp_disc_present: 1; /* Changer can report exact contents
-				      of slots. */
-	__u8 limit_nframes    : 1; /* Drive does not provide data in
-				      multiples of SECTOR_SIZE when more
-				      than one interrupt is needed. */
-	__u8 seeking          : 1; /* Seeking in progress */
-	__u8 reserved         : 6;
-	byte max_speed; 	   /* Max speed of the drive */
+	__u8 drq_interrupt	: 1; /* Device sends an interrupt when ready
+					for a packet command. */
+	__u8 no_doorlock	: 1; /* Drive cannot lock the door. */
+	__u8 no_eject		: 1; /* Drive cannot eject the disc. */
+	__u8 nec260		: 1; /* Drive is a pre-1.2 NEC 260 drive. */
+	__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. */
+	__u8 is_changer		: 1; /* Drive is a changer. */
+	__u8 cd_r		: 1; /* Drive can write to CD-R media . */
+	__u8 cd_rw		: 1; /* Drive can write to CD-R/W media . */
+	__u8 dvd		: 1; /* Drive is a DVD-ROM */
+	__u8 dvd_r		: 1; /* Drive can write DVD-R */
+	__u8 dvd_ram		: 1; /* Drive can write DVD-RAM */
+	__u8 test_write		: 1; /* Drive can fake writes */
+	__u8 supp_disc_present	: 1; /* Changer can report exact contents
+					of slots. */
+	__u8 limit_nframes	: 1; /* Drive does not provide data in
+					multiples of SECTOR_SIZE when more
+					than one interrupt is needed. */
+	__u8 seeking		: 1; /* Seeking in progress */
+	__u8 audio_play		: 1; /* can do audio related commands */
+	__u8 close_tray		: 1; /* can close the tray */
+	__u8 reserved		: 4;
+	byte max_speed;		     /* Max speed of the drive */
 };
 #define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags))
 
@@ -285,6 +228,9 @@
 } mechtype_t;
 
 
+/* This should probably go into cdrom.h along with the other
+ * generic stuff now in the Mt. Fuji spec.
+ */
 struct atapi_capabilities_page {
 #if defined(__BIG_ENDIAN_BITFIELD)
 	__u8 parameters_saveable : 1;
@@ -518,9 +464,11 @@
 
 #if defined(__BIG_ENDIAN_BITFIELD)
 	__u8 mech_state    : 3;
-	__u8 reserved1     : 5;
+	__u8 door_open     : 1;
+	__u8 reserved1     : 4;
 #elif defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8 reserved1     : 5;
+	__u8 reserved1     : 4;
+	__u8 door_open     : 1;
 	__u8 mech_state    : 3;
 #else
 #error "Please fix <asm/byteorder.h>"
@@ -604,11 +552,85 @@
 
 #define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
 
+/* This stuff should be in cdrom.h, since it is now generic... */
+
+/* ATAPI sense keys (from table 140 of ATAPI 2.6) */
+#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
+
+ 
+
+/* This stuff should be in cdrom.h, since it is now generic... */
 #if VERBOSE_IDE_CD_ERRORS
 
-/* From Table 124 of the ATAPI 1.2 spec.
-   Unchanged in Table 140 of the ATAPI 2.6 draft standard. */
+ /* The generic packet command opcodes for CD/DVD Logical Units,
+ * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ 
+const struct {
+	unsigned short packet_command;
+	const char * const text;
+} packet_command_texts[] = {
+	{ GPCMD_TEST_UNIT_READY, "Test Unit Ready" },
+	{ GPCMD_REQUEST_SENSE, "Request Sense" },
+	{ GPCMD_FORMAT_UNIT, "Format Unit" },
+	{ GPCMD_INQUIRY, "Inquiry" },
+	{ GPCMD_START_STOP_UNIT, "Start/Stop Unit" },
+	{ GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
+	{ GPCMD_READ_FORMAT_CAPACITIES, "Read Format Capacities" },
+	{ GPCMD_READ_CDVD_CAPACITY, "Read Cd/Dvd Capacity" },
+	{ GPCMD_READ_10, "Read 10" },
+	{ GPCMD_WRITE_10, "Write 10" },
+	{ GPCMD_SEEK, "Seek" },
+	{ GPCMD_WRITE_AND_VERIFY_10, "Write and Verify 10" },
+	{ GPCMD_VERIFY_10, "Verify 10" },
+	{ GPCMD_FLUSH_CACHE, "Flush Cache" },
+	{ GPCMD_READ_SUBCHANNEL, "Read Subchannel" },
+	{ GPCMD_READ_TOC_PMA_ATIP, "Read Table of Contents" },
+	{ GPCMD_READ_HEADER, "Read Header" },
+	{ GPCMD_PLAY_AUDIO_10, "Play Audio 10" },
+	{ GPCMD_GET_CONFIGURATION, "Get Configuration" },
+	{ GPCMD_PLAY_AUDIO_MSF, "Play Audio MSF" },
+	{ GPCMD_PLAYAUDIO_TI, "Play Audio TrackIndex" },
+	{ GPCMD_GET_EVENT_STATUS_NOTIFICATION, "Get Event Status Notification" },
+	{ GPCMD_PAUSE_RESUME, "Pause/Resume" },
+	{ GPCMD_STOP_PLAY_SCAN, "Stop Play/Scan" },
+	{ GPCMD_READ_DISC_INFO, "Read Disc Info" },
+	{ GPCMD_READ_TRACK_RZONE_INFO, "Read Track Rzone Info" },
+	{ GPCMD_RESERVE_RZONE_TRACK, "Reserve Rzone Track" },
+	{ GPCMD_SEND_OPC, "Send OPC" },
+	{ GPCMD_MODE_SELECT_10, "Mode Select 10" },
+	{ GPCMD_REPAIR_RZONE_TRACK, "Repair Rzone Track" },
+	{ GPCMD_MODE_SENSE_10, "Mode Sense 10" },
+	{ GPCMD_CLOSE_TRACK, "Close Track" },
+	{ GPCMD_BLANK, "Blank" },
+	{ GPCMD_SEND_EVENT, "Send Event" },
+	{ GPCMD_SEND_KEY, "Send Key" },
+	{ GPCMD_REPORT_KEY, "Report Key" },
+	{ GPCMD_LOAD_UNLOAD, "Load/Unload" },
+	{ GPCMD_SET_READ_AHEAD, "Set Read-ahead" },
+	{ GPCMD_READ_12, "Read 12" },
+	{ GPCMD_GET_PERFORMANCE, "Get Performance" },
+	{ GPCMD_SEND_DVD_STRUCTURE, "Send DVD Structure" },
+	{ GPCMD_READ_DVD_STRUCTURE, "Read DVD Structure" },
+	{ GPCMD_SET_STREAMING, "Set Streaming" },
+	{ GPCMD_READ_CD_MSF, "Read CD MSF" },
+	{ GPCMD_SCAN, "Scan" },
+	{ GPCMD_SET_SPEED, "Set Speed" },
+	{ GPCMD_PLAY_CD, "Play CD" },
+	{ GPCMD_MECHANISM_STATUS, "Mechanism Status" },
+	{ GPCMD_READ_CD, "Read CD" },
+};
+
+
 
+/* From Table 303 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
 const char * const sense_key_texts[16] = {
 	"No sense data",
 	"Recovered error",
@@ -628,162 +650,108 @@
 	"(reserved)",
 };
 
-
-/* From Table 37 of the ATAPI 2.6 draft standard. */
+/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
 const struct {
-	unsigned short packet_command;
-	const char * const text;
-} packet_command_texts[] = {
-	{ TEST_UNIT_READY, "Test Unit Ready" },
-	{ REQUEST_SENSE, "Request Sense" },
-	{ INQUIRY, "Inquiry" },
-	{ START_STOP, "Start Stop Unit" },
-	{ ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
-	{ READ_CAPACITY, "Read CD-ROM Capacity" },
-	{ READ_10, "Read(10)" },
-	{ SEEK, "Seek" },
-	{ SCMD_READ_TOC, "Read TOC" },
-	{ SCMD_READ_SUBCHANNEL, "Read Sub-Channel" },
-	{ READ_HEADER, "Read Header" },
-	{ STOP_PLAY_SCAN, "Stop Play/Scan" },
-	{ SCMD_PLAYAUDIO10, "Play Audio" },
-	{ SCMD_PLAYAUDIO_MSF, "Play Audio MSF" },
-	{ SCMD_PAUSE_RESUME, "Pause/Resume" },
-	{ MODE_SELECT_10, "Mode Select" },
-	{ MODE_SENSE_10, "Mode Sense" },
-	{ LOAD_UNLOAD, "Load/Unload CD" },
-	{ READ_12, "Read(12)" },
-	{ READ_CD_MSF, "Read CD MSF" },
-	{ SCAN, "Scan" },
-	{ SET_CD_SPEED, "Set CD Speed" },
-	{ PLAY_CD, "Play CD" },
-	{ MECHANISM_STATUS, "Mechanism Status" },
-	{ READ_CD, "Read CD" },
-	{ DVD_GET_PERFORMANCE, "Get Performance" },
-};
-
-
-/* From Table 125 of the ATAPI 1.2 spec.,
-   with additions from Tables 141 and 142 of the ATAPI 2.6 draft standard. */
-
-const struct {
-	unsigned short asc_ascq;
+	unsigned long asc_ascq;
 	const char * const 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" },
-
-	{ 0x0100, "Mechanical positioning or changer error" },
-
-	{ 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" },
-
-	{ 0x0501, "Media load - eject failed" },
-
-	{ 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 or changer 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" },
-	/* Following two not in 2.6. */
-	{ 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" },
-	/* Following code not in 2.6. */
-	{ 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" },
-
-	/* Following code not in 2.6. */
-	{ 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" },
-	/* Following two not in 2.6. */
-	{ 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" },
-
-	/* Following two not in 2.6. */
-	{ 0x5b00, "Threshold condition met" },
-	{ 0x5c00, "Status change" },
-
-	{ 0x6300, "End of user area encountered on this track" },
-
-	{ 0x6400, "Illegal mode for this track or incompatible medium" },
-
-	/* Following error is misspelled in ATAPI 2.6 */
-	{ 0xb900, "Play operation oborted [sic]" },
-
-	{ 0xbf00, "Loss of streaming" },
+	{ 0x000000, "No additional sense information" },
+	{ 0x000011, "Play operation in progress" },
+	{ 0x000012, "Play operation paused" },
+	{ 0x000013, "Play operation successfully completed" },
+	{ 0x000014, "Play operation stopped due to error" },
+	{ 0x000015, "No current audio status to return" },
+	{ 0x011700, "Recovered data with no error correction applied" },
+	{ 0x011701, "Recovered data with retries" },
+	{ 0x011702, "Recovered data with positive head offset" },
+	{ 0x011703, "Recovered data with negative head offset" },
+	{ 0x011704, "Recovered data with retries and/or CIRC applied" },
+	{ 0x011705, "Recovered data using previous sector ID" },
+	{ 0x011800, "Recovered data with error correction applied" },
+	{ 0x011801, "Recovered data with error correction and retries applied"},
+	{ 0x011802, "Recovered data - the data was auto-reallocated" },
+	{ 0x011803, "Recovered data with CIRC" },
+	{ 0x011804, "Recovered data with L-EC" },
+	{ 0x015d00, 
+	    "Failure prediction threshold exceeded - Predicted logical unit failure" },
+	{ 0x015d01, 
+	    "Failure prediction threshold exceeded - Predicted media failure" },
+	{ 0x015dff, "Failure prediction threshold exceeded - False" },
+	{ 0x020400, "Logical unit not ready - cause not reportable" },
+	/* Following is misspelled in ATAPI 2.6, _and_ in Mt. Fuji */
+	{ 0x020401,
+	  "Logical unit not ready - in progress [sic] of becoming ready" },
+	{ 0x020402, "Logical unit not ready - initializing command required" },
+	{ 0x020403, "Logical unit not ready - manual intervention required" },
+	{ 0x020404, "In process of becoming ready - writing" },
+	{ 0x020600, "No reference position found (media may be upside down)" },
+	{ 0x023000, "Incompatible medium installed" },
+	{ 0x023a00, "Medium not present" },
+	{ 0x025300, "Media load or eject failed" },
+	{ 0x025700, "Unable to recover table of contents" },
+	{ 0x031100, "Unrecovered read error" },
+	{ 0x031106, "CIRC unrecovered error" },
+	{ 0x033101, "Format command failed" },
+	{ 0x040200, "No seek complete" },
+	{ 0x040300, "Write fault" },
+	{ 0x040900, "Track following error" },
+	{ 0x040901, "Tracking servo failure" },
+	{ 0x040902, "Focus servo failure" },
+	{ 0x040903, "Spindle servo failure" },
+	{ 0x041500, "Random positioning error" },
+	{ 0x041501, "Mechanical positioning or changer error" },
+	{ 0x041502, "Positioning error detected by read of medium" },
+	{ 0x043c00, "Mechanical positioning or changer error" },
+	{ 0x044000, "Diagnostic failure on component (ASCQ)" },
+	{ 0x044400, "Internal CD/DVD logical unit failure" },
+	{ 0x04b600, "Media load mechanism failed" },
+	{ 0x051a00, "Parameter list length error" },
+	{ 0x052000, "Invalid command operation code" },
+	{ 0x052c00, "Command sequence error" },
+	{ 0x052100, "Logical block address out of range" },
+	{ 0x052400, "Invalid field in command packet" },
+	{ 0x052600, "Invalid field in parameter list" },
+	{ 0x052601, "Parameter not supported" },
+	{ 0x052602, "Parameter value invalid" },
+	{ 0x052700, "Write protected media" },
+	{ 0x053001, "Cannot read medium - unknown format" },
+	{ 0x053002, "Cannot read medium - incompatible format" },
+	{ 0x053900, "Saving parameters not supported" },
+	{ 0x054e00, "Overlapped commands attempted" },
+	{ 0x055302, "Medium removal prevented" },
+	{ 0x055500, "System resource failure" },
+	{ 0x056300, "End of user area encountered on this track" },
+	{ 0x056400, "Illegal mode for this track or incompatible medium" },
+	{ 0x056f00, 
+	    "Copy protection key exchange failure - Authentication failure" },
+	{ 0x056f01, "Copy protection key exchange failure - Key not present" },
+	{ 0x056f02, 
+	    "Copy protection key exchange failure - Key not established" },
+	{ 0x05bf00, "Loss of streaming" },
+	{ 0x062800, "Not ready to ready transition, medium may have changed" },
+	{ 0x062900, "Power on, reset or hardware reset occurred" },
+	{ 0x062a00, "Parameters changed" },
+	{ 0x062a01, "Mode parameters changed" },
+	{ 0x062e00, "Insufficient time for operation" },
+	{ 0x063f00, "Logical unit operating conditions have changed" },
+	{ 0x063f01, "Microcode has been changed" },
+	{ 0x065a00, "Operator request or state change input (unspecified)" },
+	{ 0x065a01, "Operator medium removal request" },
+	{ 0x0bb900, "Play operation aborted" },
+
+	/* Here we use 0xff for the key (not a valid key) to signify
+	 * that these can have _any_ key value associated with them... */
+	{ 0xff0401, "Logical unit is in process of becoming ready" },
+	{ 0xff0400, "Logical unit not ready, cause not reportable" },
+	{ 0xff0402, "Logical unit not ready, initializing command required" },
+	{ 0xff0403, "Logical unit not ready, manual intervention required" },
+	{ 0xff0500, "Logical unit does not respond to selection" },
+	{ 0xff0800, "Logical unit communication failure" },
+	{ 0xff0802, "Logical unit communication parity error" },
+	{ 0xff0801, "Logical unit communication time-out" },
+	{ 0xff2500, "Logical unit not supported" },
+	{ 0xff4c00, "Logical unit failed self-configuration" },
+	{ 0xff3e00, "Logical unit has not self-configured yet" },
 };
 #endif
 
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c
--- /tmp/linux-2.2.12/drivers/block/paride/pcd.c	Mon Aug 23 13:32:23 1999
+++ linux/drivers/block/paride/pcd.c	Sat Aug 28 14:09:36 1999
@@ -214,8 +214,11 @@
 static int pcd_get_mcn (struct cdrom_device_info *cdi, struct cdrom_mcn *mcn);
 static int pcd_audio_ioctl(struct cdrom_device_info *cdi,
 				unsigned int cmd, void *arg);
+static int pcd_packet(struct cdrom_device_info *cdi,
+				struct cdrom_generic_command *cgc);
 
 static int 	pcd_detect(void);
+static void 	pcd_probe_capabilities(void);
 static void     do_pcd_read_drq(void);
 static void 	do_pcd_request(void);
 static void 	do_pcd_read(void);
@@ -276,14 +279,18 @@
 	pcd_drive_reset,
 	pcd_audio_ioctl,
 	0,			/* dev_ioctl */
-	CDC_CLOSE_TRAY    |
-	CDC_OPEN_TRAY     |
-	CDC_LOCK          |
-	CDC_MCN		  |
-	CDC_MEDIA_CHANGED |
-	CDC_RESET	  |
-	CDC_PLAY_AUDIO,
-	0
+	CDC_CLOSE_TRAY	   |
+	CDC_OPEN_TRAY	   |
+	CDC_LOCK	   |
+	CDC_MCN		   |
+	CDC_MEDIA_CHANGED  |
+	CDC_RESET	   |
+	CDC_PLAY_AUDIO	   |
+	CDC_GENERIC_PACKET |
+	CDC_CD_R	   |
+	CDC_CD_RW,
+	0,
+	pcd_packet,
 };
 
 static void pcd_init_units( void )
@@ -325,6 +332,9 @@
 
 	if (pcd_detect()) return -1;
 
+	/* get the atapi capabilities page */
+	pcd_probe_capabilities();
+
 	if (register_blkdev(MAJOR_NR,name,&cdrom_fops)) {
 		printk("pcd: unable to get major number %d\n",MAJOR_NR);
 		return -1;
@@ -525,6 +535,16 @@
 	return r;
 }
 
+static int pcd_packet(struct cdrom_device_info *cdi,
+				struct cdrom_generic_command *cgc)
+{
+	char	*un_cmd;
+	int	unit = DEVICE_NR(cdi->dev);
+
+	un_cmd = cgc->cmd;
+	return pcd_atapi(unit,un_cmd,cgc->buflen,cgc->buffer, "generic packet");
+}
+
 #define DBMSG(msg)	((verbose>1)?(msg):NULL)
 
 static int pcd_media_changed(struct cdrom_device_info *cdi, int slot_nr)
@@ -667,6 +687,32 @@
 	return -1;
 }
 
+static void pcd_probe_capabilities( void )
+
+{	int	unit, r;
+	char	buffer[32];
+	char 	cmd[12]={0x5a,1<<3,0x2a,0,0,0,0,18,0,0,0,0};
+
+	for (unit=0;unit<PCD_UNITS;unit++) {
+		if (!PCD.present) continue;
+		r = pcd_atapi(unit,cmd,18, buffer,"mode sense capabilities");
+		if (r) continue;
+		/* we should now have the cap page */
+		if ((buffer[11] & 1) == 0)
+			PCD.info.mask |= CDC_CD_R;
+		if ((buffer[11] & 2) == 0)
+			PCD.info.mask |= CDC_CD_RW;
+		if ((buffer[12] & 1) == 0)
+			PCD.info.mask |= CDC_PLAY_AUDIO;
+		if ((buffer[14] & 1) == 0)
+			PCD.info.mask |= CDC_LOCK;
+		if ((buffer[14] & 8) == 0)
+			PCD.info.mask |= CDC_OPEN_TRAY;
+		if ((buffer[14] >> 6) == 0)
+			PCD.info.mask |= CDC_CLOSE_TRAY;
+	}
+}
+
 static int pcd_detect( void )
 
 {	char    id[18];
@@ -836,66 +882,9 @@
  
     	switch (cmd) { 
     
-	case CDROMPAUSE: 
-
-	{       char cmd[12]={SCMD_PAUSE_RESUME,0,0,0,0,0,0,0,0,0,0,0};
-
-		return (pcd_atapi(unit,cmd,0,NULL,"pause")) * EIO;
-	}
-
-	case CDROMRESUME:
-	
-	{       char cmd[12]={SCMD_PAUSE_RESUME,0,0,0,0,0,0,0,1,0,0,0};
-
-		return (pcd_atapi(unit,cmd,0,NULL,"resume")) * EIO;
-	}
-	
-	case CDROMPLAYMSF:
-
-	{	char cmd[12]={SCMD_PLAYAUDIO_MSF,0,0,0,0,0,0,0,0,0,0,0};
-		struct cdrom_msf* msf = (struct cdrom_msf*)arg;
-
-		cmd[3] = msf->cdmsf_min0;
-		cmd[4] = msf->cdmsf_sec0;
-		cmd[5] = msf->cdmsf_frame0;
-		cmd[6] = msf->cdmsf_min1;
-		cmd[7] = msf->cdmsf_sec1;
-		cmd[8] = msf->cdmsf_frame1;
-	
-		return (pcd_atapi(unit,cmd,0,NULL,"play msf")) * EIO;
-    	}
-
-    	case CDROMPLAYBLK:
-
-    	{	char cmd[12]={SCMD_PLAYAUDIO10,0,0,0,0,0,0,0,0,0,0,0};
-		struct cdrom_blk* blk = (struct cdrom_blk*)arg;
-
-		cmd[2] = blk->from >> 24;
-		cmd[3] = blk->from >> 16;
-		cmd[4] = blk->from >> 8;
-		cmd[5] = blk->from;
-		cmd[7] = blk->len >> 8;
-		cmd[8] = blk->len;
-	
-		return (pcd_atapi(unit,cmd,0,NULL,"play block")) * EIO;
-    	}
-		
-    	case CDROMPLAYTRKIND:
-
-    	{	char cmd[12]={SCMD_PLAYAUDIO_TI,0,0,0,0,0,0,0,0,0,0,0};
-		struct cdrom_ti* ti = (struct cdrom_ti*)arg;
-
-		cmd[4] = ti->cdti_trk0;
-		cmd[5] = ti->cdti_ind0;
-		cmd[7] = ti->cdti_trk1;
-		cmd[8] = ti->cdti_ind1;
-
-		return (pcd_atapi(unit,cmd,0,NULL,"play track")) * EIO;
-    	}
-	
     	case CDROMREADTOCHDR:
     
-	{	char cmd[12]={SCMD_READ_TOC,0,0,0,0,0,0,0,12,0,0,0};
+	{	char cmd[12]={GPCMD_READ_TOC_PMA_ATIP,0,0,0,0,0,0,0,12,0,0,0};
 		struct cdrom_tochdr* tochdr = (struct cdrom_tochdr*)arg;
 		char buffer[32];
 		int r;
@@ -910,7 +899,7 @@
 	
     	case CDROMREADTOCENTRY:
 
-    	{	char cmd[12]={SCMD_READ_TOC,0,0,0,0,0,0,0,12,0,0,0};
+    	{	char cmd[12]={GPCMD_READ_TOC_PMA_ATIP,0,0,0,0,0,0,0,12,0,0,0};
 
 		struct cdrom_tocentry* tocentry = (struct cdrom_tocentry*)arg;
 		unsigned char buffer[32];
@@ -936,97 +925,6 @@
                 return r * EIO;
         }
 
-    	case CDROMSTOP:
-
-	{	char cmd[12]={0x1b,1,0,0,0,0,0,0,0,0,0,0};
-
-                return (pcd_atapi(unit,cmd,0,NULL,"stop")) * EIO;
-        }                                                         
-	
-    	case CDROMSTART:
-
-        {       char cmd[12]={0x1b,1,0,0,1,0,0,0,0,0,0,0};
-
-                return (pcd_atapi(unit,cmd,0,NULL,"start")) * EIO;
-        } 
-	
-    	case CDROMVOLCTRL:
-
-	{	char cmd[12]={0x5a,0,0,0,0,0,0,0,0,0,0,0};
-		char buffer[32];
-		char mask[32];
-		struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg;
-
-		cmd[2] = 0xe;
-		cmd[4] = 28;
-
-                if (pcd_atapi(unit,cmd,28,buffer,"mode sense vol")) 
-			return -EIO;
-
-		cmd[2] = 0x4e;
-	
-		if (pcd_atapi(unit,cmd,28,buffer,"mode sense vol mask"))
-			return -EIO;
-	
-		buffer[0] = 0;
-	
-		buffer[21] = volctrl->channel0 & mask[21];
-		buffer[23] = volctrl->channel1 & mask[23];
-		buffer[25] = volctrl->channel2 & mask[25];
-		buffer[27] = volctrl->channel3 & mask[27];
-	
-		cmd[0] = 0x55;
-		cmd[1] = 0x10;
-
-		return pcd_atapi(unit,cmd,28,buffer,"mode select vol") * EIO;
-    	}
-
-    	case CDROMVOLREAD:
-
-        {       char cmd[12]={0x5a,0,0,0,0,0,0,0,0,0,0,0};
-                char buffer[32];
-                struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg;
-                int     r;
-
-                cmd[2] = 0xe;
-                cmd[4] = 28;
-
-                r = pcd_atapi(unit,cmd,28,buffer,"mode sense vol read");
-
-		volctrl->channel0 = buffer[21];
-		volctrl->channel1 = buffer[23];
-		volctrl->channel2 = buffer[25];
-		volctrl->channel3 = buffer[27];
-
-                return r * EIO;
-        }
-  
-	
-    	case CDROMSUBCHNL:
-
-	{	char cmd[12]={SCMD_READ_SUBCHANNEL,2,0x40,1,0,0,0,0,16,0,0,0};
-		struct cdrom_subchnl* subchnl = (struct cdrom_subchnl*)arg;
-		char buffer[32];
-	
-                if (pcd_atapi(unit,cmd,16,buffer,"read subchannel"))
-                        return -EIO;
-   	
-		subchnl->cdsc_audiostatus = buffer[1];
-		subchnl->cdsc_format = CDROM_MSF;
-		subchnl->cdsc_ctrl = buffer[5] & 0xf;
-		subchnl->cdsc_trk = buffer[6];
-		subchnl->cdsc_ind = buffer[7];
-	
-		subchnl->cdsc_reladdr.msf.minute = buffer[13];
-		subchnl->cdsc_reladdr.msf.second = buffer[14];
-		subchnl->cdsc_reladdr.msf.frame = buffer[15];
-		subchnl->cdsc_absaddr.msf.minute = buffer[9];
-		subchnl->cdsc_absaddr.msf.second = buffer[10];
-		subchnl->cdsc_absaddr.msf.frame = buffer[11];
-
-		return 0;	
-    	}
-
     	default:
 
         	return -ENOSYS;
@@ -1035,7 +933,7 @@
 	
 static int pcd_get_mcn (struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
 
-{	char 	cmd[12]={SCMD_READ_SUBCHANNEL,0,0x40,2,0,0,0,0,24,0,0,0};
+{	char 	cmd[12]={GPCMD_READ_SUBCHANNEL,0,0x40,2,0,0,0,0,24,0,0,0};
         char 	buffer[32];
 	int	k;
 	int	unit = DEVICE_NR(cdi->dev);
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- /tmp/linux-2.2.12/drivers/cdrom/cdrom.c	Mon Aug 23 13:32:23 1999
+++ linux/drivers/cdrom/cdrom.c	Thu Sep  2 22:48:45 1999
@@ -1,7 +1,7 @@
 /* linux/drivers/cdrom/cdrom.c. 
    Copyright (c) 1996, 1997 David A. van Leeuwen.
    Copyright (c) 1997, 1998 Erik Andersen <andersee@debian.org>
-   Copyright (c) 1998, 1999 Jens Axboe
+   Copyright (c) 1998, 1999 Jens Axboe <axboe@image.dk>
 
    May be copied or modified under the terms of the GNU General Public
    License.  See linux/COPYING for more information.
@@ -22,12 +22,6 @@
  based tunable parameters such as whether the tray should auto-close for
  that drive. Suggestions (or patches) for this welcome!
 
- -- Change the CDROMREADMODE1, CDROMREADMODE2, CDROMREADAUDIO, and 
- CDROMREADRAW ioctls so they go through the Uniform CD-ROM driver.
- 
- -- Sync options and capability flags.
- 
-
 
  Revision History
  ----------------------------------
@@ -125,11 +119,55 @@
   -- autoclose was mistakenly checked against CDC_OPEN_TRAY instead of
   CDC_CLOSE_TRAY.
   -- proc info didn't mask against capabilities mask.
+  
+  3.00 Aug 5, 1999 - Jens Axboe <axboe@image.dk>
+  -- Unified audio ioctl handling across CD-ROM drivers. A lot of the
+  code was duplicated before. Drives that support the generic packet
+  interface are now being fed packets from here instead.
+  -- First attempt at adding support for MMC2 commands - for DVD and
+  CD-R(W) drives. Only the DVD parts are in now - the interface used is
+  the same as for the audio ioctls.
+  -- ioctl cleanups. if a drive couldn't play audio, it didn't get
+  a change to perform device specific ioctls as well.
+  -- Defined CDROM_CAN(CDC_XXX) for checking the capabilities.
+  -- Put in sysctl files for autoclose, autoeject, check_media, debug,
+  and lock.
+  -- /proc/sys/dev/cdrom/info has been updated to also contain info about
+  CD-Rx and DVD capabilities.
+  -- Now default to checking media type.
+  -- CDROM_SEND_PACKET ioctl added. The infrastructure was in place for
+  doing this anyway, with the generic_packet addition.
+  
+  3.01 Aug 6, 1999 - Jens Axboe <axboe@image.dk>
+  -- Fix up the sysctl handling so that the option flags get set
+  correctly.
+  -- Fix up ioctl handling so the device specific ones actually get
+  called :).
+  
+  3.02 Aug 8, 1999 - Jens Axboe <axboe@image.dk>
+  -- Fixed volume control on SCSI drives (or others with longer audio
+  page).
+  -- Fixed a couple of DVD minors. Thanks to Andrew T. Veliath
+  <andrewtv@usa.net> for telling me and for having defined the various
+  DVD structures and ioctls in the first place! He designed the original
+  DVD patches for ide-cd and while I rearranged and unified them, the
+  interface is still the same.
+  
+  3.03 Sep 1, 1999 - Jens Axboe <axboe@image.dk>
+  -- Moved the rest of the audio ioctls from the CD-ROM drivers here. Only
+  CDROMREADTOCENTRY and CDROMREADTOCHDR are left.
+  -- Moved the CDROMREADxxx ioctls in here.
+  -- Defined the cdrom_get_last_written and cdrom_get_next_block as ioctls
+  and exported functions.
+  -- Erik Andersen <andersen@xmission.com> modified all SCMD_ commands
+  to now read GPCMD_ for the new generic packet interface. All low level
+  drivers are updated as well.
+  -- Various other cleanups.
 
 -------------------------------------------------------------------------*/
 
-#define REVISION "Revision: 2.55"
-#define VERSION "Id: cdrom.c 2.55 1999/04/25"
+#define REVISION "Revision: 3.03"
+#define VERSION "Id: cdrom.c 3.03 1999/09/01"
 
 /* I use an error-log mask to give fine grain control over the type of
    messages dumped to the system logs.  The available masks include: */
@@ -172,7 +210,7 @@
 static int autoclose=1;
 static int autoeject=0;
 static int lockdoor = 1;
-static int check_media_type = 0;
+static int check_media_type = 1;
 MODULE_PARM(debug, "i");
 MODULE_PARM(autoclose, "i");
 MODULE_PARM(autoeject, "i");
@@ -194,8 +232,12 @@
 #define IOCTL_OUT(arg, type, out) \
 	copy_to_user_ret((type *) arg, &out, sizeof out, -EFAULT)
 
+/* The (cdo->capability & ~cdi->mask & CDC_XXX) construct was used in
+   a lot of places. This macro makes the code more clear. */
+#define CDROM_CAN(type) (cdi->ops->capability & ~cdi->mask & type)
 
-#define FM_WRITE	0x2                 /* file mode write bit */
+/* used in the audio ioctls */
+#define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret
 
 /* Not-exported routines. */
 static int cdrom_open(struct inode *ip, struct file *fp);
@@ -208,6 +250,12 @@
 			 struct cdrom_device_ops * cdo);
 static void sanitize_format(union cdrom_addr *addr, 
 		u_char * curr, u_char requested);
+static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
+		     unsigned long arg);
+
+int cdrom_get_last_written(kdev_t dev, long *last_written);
+int cdrom_get_next_writable(kdev_t dev, long *next_writable);
+
 #ifdef CONFIG_SYSCTL
 static void cdrom_sysctl_register(void);
 #endif /* CONFIG_SYSCTL */ 
@@ -269,13 +317,14 @@
 	ENSURE(reset, CDC_RESET);
 	ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
 	ENSURE(dev_ioctl, CDC_IOCTLS);
+	ENSURE(generic_packet, CDC_GENERIC_PACKET);
 	cdi->mc_flags = 0;
 	cdo->n_minors = 0;
         cdi->options = CDO_USE_FFLAGS;
 	
-	if (autoclose==1 && cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY)
+	if (autoclose==1 && CDROM_CAN(CDC_CLOSE_TRAY))
 		cdi->options |= (int) CDO_AUTO_CLOSE;
-	if (autoeject==1 && cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)
+	if (autoeject==1 && CDROM_CAN(CDC_OPEN_TRAY))
 		cdi->options |= (int) CDO_AUTO_EJECT;
 	if (lockdoor==1)
 		cdi->options |= (int) CDO_LOCK;
@@ -347,7 +396,7 @@
 	cdinfo(CD_OPEN, "entering cdrom_open\n"); 
 	if (cdi == NULL)
 		return -ENODEV;
-	if (fp->f_mode & FM_WRITE)
+	if (fp->f_mode & FMODE_WRITE)
 		return -EROFS;
 	purpose = purpose || !(cdi->options & CDO_USE_FFLAGS);
 	if (purpose)
@@ -377,7 +426,7 @@
 		if (ret == CDS_TRAY_OPEN) {
 			cdinfo(CD_OPEN, "the tray is open...\n"); 
 			/* can/may i close it? */
-			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
+			if (CDROM_CAN(CDC_CLOSE_TRAY) &&
 			    cdi->options & CDO_AUTO_CLOSE) {
 				cdinfo(CD_OPEN, "trying to close the tray.\n"); 
 				ret=cdo->tray_move(cdi,0);
@@ -421,7 +470,11 @@
 	 * for example, need bit CDO_CHECK_TYPE cleared! */
 	if (tracks.data==0) {
 		if (cdi->options & CDO_CHECK_TYPE) {
+		    /* give people a warning shot, now that CDO_CHECK_TYPE
+		       is the default case! */
 		    cdinfo(CD_OPEN, "bummer. wrong media type.\n"); 
+		    cdinfo(CD_WARNING, "pid %d must open device O_NONBLOCK!\n",
+					(unsigned int)current->pid); 
 		    ret=-EMEDIUMTYPE;
 		    goto clean_up_and_return;
 		}
@@ -442,8 +495,7 @@
 		cdinfo(CD_OPEN, "open device failed.\n"); 
 		goto clean_up_and_return;
 	}
-	if (cdo->capability & ~cdi->mask & CDC_LOCK && 
-		cdi->options & CDO_LOCK) {
+	if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {
 			cdo->lock_door(cdi, 1);
 			cdinfo(CD_OPEN, "door locked.\n");
 	}	
@@ -457,8 +509,7 @@
 	is a goto to avoid bloating the driver with redundant code. */ 
 clean_up_and_return:
 	cdinfo(CD_WARNING, "open failed.\n"); 
-	if (cdo->capability & ~cdi->mask & CDC_LOCK && 
-		cdi->options & CDO_LOCK) {
+	if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {
 			cdo->lock_door(cdi, 0);
 			cdinfo(CD_OPEN, "door unlocked.\n");
 	}
@@ -482,7 +533,7 @@
 		if (ret == CDS_TRAY_OPEN) {
 			cdinfo(CD_OPEN, "the tray is open...\n"); 
 			/* can/may i close it? */
-			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
+			if (CDROM_CAN(CDC_CLOSE_TRAY) &&
 			    cdi->options & CDO_AUTO_CLOSE) {
 				cdinfo(CD_OPEN, "trying to close the tray.\n"); 
 				ret=cdo->tray_move(cdi,0);
@@ -553,8 +604,7 @@
 		if (sb) invalidate_inodes(sb);
 		invalidate_buffers(dev);
 		if (opened_for_data &&
-		    cdi->options & CDO_AUTO_EJECT &&
-		    cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)
+		    cdi->options & CDO_AUTO_EJECT && CDROM_CAN(CDC_OPEN_TRAY))
 			cdo->tray_move(cdi, 1);
 	}
 	return 0;
@@ -572,7 +622,7 @@
 	unsigned int mask = (1 << (queue & 1));
 	int ret = !!(cdi->mc_flags & mask);
 
-	if (!(cdi->ops->capability & ~cdi->mask & CDC_MEDIA_CHANGED))
+	if (!CDROM_CAN(CDC_MEDIA_CHANGED))
 	    return ret;
 	/* changed since last call? */
 	if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
@@ -594,7 +644,7 @@
 		return 0;
 	if (cdi->ops->media_changed == NULL)
 		return 0;
-	if (!(cdi->ops->capability & ~cdi->mask & CDC_MEDIA_CHANGED))
+	if (!CDROM_CAN(CDC_MEDIA_CHANGED))
 	    return 0;
 	return (media_changed(cdi, 0));
 }
@@ -610,7 +660,7 @@
 	tracks->xa=0;
 	tracks->error=0;
 	cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); 
-        if (!(cdi->ops->capability & ~cdi->mask & CDC_PLAY_AUDIO)) { 
+        if (!CDROM_CAN(CDC_PLAY_AUDIO)) { 
                 tracks->error=CDS_NO_INFO;
                 return;
         }        
@@ -684,6 +734,422 @@
 	*curr = requested;
 }
 
+/* DVD handling */
+
+#define copy_key(dest,src)	memcpy((dest), (src), sizeof(dvd_key))
+#define copy_chal(dest,src)	memcpy((dest), (src), sizeof(dvd_challenge))
+
+static void setup_report_key (struct cdrom_generic_command *cgc, unsigned agid, unsigned type)
+{
+	cgc->cmd[0] = GPCMD_REPORT_KEY;
+	cgc->cmd[10] = type | (agid << 6);
+}
+
+static void setup_send_key (struct cdrom_generic_command *cgc, unsigned agid, unsigned type)
+{
+	cgc->cmd[0] = GPCMD_SEND_KEY;
+	cgc->cmd[10] = type | (agid << 6);
+}
+
+static int dvd_do_auth (struct cdrom_device_info *cdi, dvd_authinfo *ai)
+{
+	int ret;
+	u_char buf[20];
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(&cgc, 0, sizeof(cgc));
+	memset(buf, 0x93, sizeof(buf));
+	cgc.buffer = buf;
+
+	switch (ai->type) {
+	/* LU data send */
+	case DVD_LU_SEND_AGID:
+		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_AGID\n"); 
+		setup_report_key (&cgc, 0, 0);
+		cgc.buflen = cgc.cmd[9] = 8;
+
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret)
+			return ret;
+
+		ai->lsa.agid = buf[7] >> 6;
+		/* Returning data, let host change state */
+		break;
+
+	case DVD_LU_SEND_KEY1:
+		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_KEY1\n"); 
+		setup_report_key (&cgc, ai->lsk.agid, 2);
+		cgc.buflen = cgc.cmd[9] = 12;
+
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret) return ret;
+
+		copy_key(ai->lsk.key, &buf[4]);
+		/* Returning data, let host change state */
+		break;
+
+	case DVD_LU_SEND_CHALLENGE:
+		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_CHALLENGE\n"); 
+		setup_report_key (&cgc, ai->lsc.agid, 1);
+		cgc.buflen = cgc.cmd[9] = 16;
+
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret) return ret;
+
+		copy_chal(ai->lsc.chal, &buf[4]);
+		/* Returning data, let host change state */
+		break;
+
+	/* Post-auth key */
+	case DVD_LU_SEND_TITLE_KEY:
+		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_TITLE_KEY\n"); 
+		setup_report_key (&cgc, ai->lstk.agid, 4);
+		cgc.cmd[5] = ai->lstk.lba;
+		cgc.cmd[4] = ai->lstk.lba >> 8;
+		cgc.cmd[3] = ai->lstk.lba >> 16;
+		cgc.cmd[2] = ai->lstk.lba >> 24;
+		cgc.buflen = cgc.cmd[9] = 12;
+
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret) return ret;
+
+		ai->lstk.cpm = (buf[4] >> 7) & 1;
+		ai->lstk.cp_sec = (buf[4] >> 6) & 1;
+		ai->lstk.cgms = (buf[4] >> 4) & 3;
+		copy_key (ai->lstk.title_key, &buf[5]);
+		/* Returning data, let host change state */
+		break;
+
+	case DVD_LU_SEND_ASF:
+		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_ASF\n"); 
+		setup_report_key (&cgc, ai->lsasf.asf, 5);
+		cgc.buflen = cgc.cmd[9] = 8;
+
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret) return ret;
+
+		ai->lsasf.asf = buf[7] & 1;
+		break;
+
+	/* LU data receive (LU changes state) */
+	case DVD_HOST_SEND_CHALLENGE:
+		cdinfo(CD_DO_IOCTL, "entering DVD_HOST_SEND_CHALLENGE\n"); 
+		setup_send_key (&cgc, ai->hsc.agid, 1);
+		cgc.buflen = cgc.cmd[9] = 16;
+		buf[1] = 14;
+		copy_chal (&buf[4], ai->hsc.chal);
+
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret) return ret;
+
+		ai->type = DVD_LU_SEND_KEY1;
+		break;
+
+	case DVD_HOST_SEND_KEY2:
+		cdinfo(CD_DO_IOCTL, "entering DVD_HOST_SEND_KEY2\n"); 
+		setup_send_key (&cgc, ai->hsk.agid, 3);
+		cgc.buflen = cgc.cmd[9] = 12;
+		buf[1] = 10;
+		copy_key (&buf[4], ai->hsk.key);
+
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret) {
+			ai->type = DVD_AUTH_FAILURE;
+			return ret;
+		}
+		ai->type = DVD_AUTH_ESTABLISHED;
+		break;
+
+	/* Misc */
+	case DVD_INVALIDATE_AGID:
+		cdinfo(CD_DO_IOCTL, "entering DVD_INVALIDATE_AGID\n"); 
+		setup_report_key (&cgc, ai->lsa.agid, 0x3f);
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (ret) return ret;
+		break;
+
+	default:
+		cdinfo(CD_WARNING, "Invalid DVD key ioctl (%d)\n", ai->type);
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+static int dvd_read_physical (struct cdrom_device_info *cdi, dvd_struct *s)
+{
+	int ret, i;
+	u_char buf[4 + 4 * 20], *base;
+	struct dvd_layer *layer;
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(buf, 0, sizeof(buf));
+	memset(&cgc, 0, sizeof(cgc));
+	cgc.buffer = buf;
+	cgc.buflen = sizeof(buf);
+	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+	cgc.cmd[6] = s->physical.layer_num;
+	cgc.cmd[7] = s->type;
+	cgc.cmd[9] = cgc.buflen & 0xff;
+
+	ret = cdo->generic_packet(cdi, &cgc);
+	if (ret) return ret;
+
+	base = &buf[4];
+	layer = &s->physical.layer[0];
+
+	/* place the data... really ugly, but at least we won't have to
+	   worry about endianess in userspace or here. */
+	for (i = 0; i < 4; ++i, base += 20, ++layer) {
+		memset (layer, 0, sizeof (*layer));
+		layer->book_version = base[0] & 0xf;
+		layer->book_type = base[0] >> 4;
+		layer->min_rate = base[1] & 0xf;
+		layer->disc_size = base[1] >> 4;
+		layer->layer_type = base[2] & 0xf;
+		layer->track_path = (base[2] >> 4) & 1;
+		layer->nlayers = (base[2] >> 5) & 3;
+		layer->track_density = base[3] & 0xf;
+		layer->linear_density = base[3] >> 4;
+		layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
+		layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
+		layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
+		layer->bca = base[16] >> 7;
+	}
+
+	return 0;
+}
+
+static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s)
+{
+	int ret;
+	u_char buf[8];
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(buf, 0, sizeof(buf));
+	memset(&cgc, 0, sizeof(cgc));
+	cgc.buffer = buf;
+	cgc.buflen = sizeof(buf);
+	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+	cgc.cmd[6] = s->copyright.layer_num;
+	cgc.cmd[7] = s->type;
+	cgc.cmd[8] = cgc.buflen >> 8;
+	cgc.cmd[9] = cgc.buflen & 0xff;
+
+	ret = cdo->generic_packet(cdi, &cgc);
+	if (ret)
+		return ret;
+
+	s->copyright.cpst = buf[4];
+	s->copyright.rmi = buf[5];
+
+	return 0;
+}
+
+static int dvd_read_disckey (struct cdrom_device_info *cdi, dvd_struct *s)
+{
+	int ret;
+	u_char buf[4 + 2048];
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(buf, 0, sizeof (buf));
+	memset(&cgc, 0, sizeof (cgc));
+
+	cgc.buffer = buf;
+	cgc.buflen = sizeof(buf);
+	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+	cgc.cmd[7] = s->type;
+	cgc.cmd[8] = sizeof(buf) >> 8;
+	cgc.cmd[9] = cgc.buflen & 0xff;
+	cgc.cmd[10] = s->disckey.agid << 6;
+
+	ret = cdo->generic_packet(cdi, &cgc);
+	if (ret) return ret;
+
+	memcpy (s->disckey.value, &buf[4], 2048);
+
+	return 0;
+}
+
+static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s)
+{
+	int ret;
+	u_char buf[4 + 188];
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(buf, 0, sizeof (buf));
+	memset(&cgc, 0, sizeof (cgc));
+	cgc.buffer = buf;
+	cgc.buflen = sizeof(buf);
+	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+	cgc.cmd[7] = s->type;
+	cgc.cmd[9] = cgc.buflen = 0xff;
+
+	ret = cdo->generic_packet(cdi, &cgc);
+	if (ret) return ret;
+
+	s->bca.len = buf[0] << 8 | buf[1];
+	if (s->bca.len < 12 || s->bca.len > 188) {
+		cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len);
+		return -EIO;
+	}
+	memcpy(s->bca.value, &buf[4], s->bca.len);
+
+	return 0;
+}
+
+static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
+{
+	int ret;
+	u_char buf[4 + 2048];
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(buf, 0, sizeof(buf));
+	memset(&cgc, 0, sizeof(cgc));
+	cgc.buffer = buf;
+	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+	cgc.cmd[7] = s->type;
+	cgc.buflen = sizeof(buf);
+	cgc.cmd[8] = sizeof(buf) >> 8;
+	cgc.cmd[9] = cgc.buflen & 0xff;
+
+	ret = cdo->generic_packet(cdi, &cgc);
+	if (ret) return ret;
+
+	s->manufact.len = buf[0] << 8 | buf[1];
+	if (s->manufact.len < 0 || s->manufact.len > 2048) {
+		cdinfo(CD_WARNING, "Recieved invalid manufacture info length (%d)\n", s->bca.len);
+		return -EIO;
+	}
+	memcpy(s->manufact.value, &buf[4], s->manufact.len);
+
+	return 0;
+}
+
+static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s)
+{
+	switch (s->type) {
+	case DVD_STRUCT_PHYSICAL:
+		return dvd_read_physical(cdi, s);
+
+	case DVD_STRUCT_COPYRIGHT:
+		return dvd_read_copyright(cdi, s);
+
+	case DVD_STRUCT_DISCKEY:
+		return dvd_read_disckey(cdi, s);
+
+	case DVD_STRUCT_BCA:
+		return dvd_read_bca(cdi, s);
+
+	case DVD_STRUCT_MANUFACT:
+		return dvd_read_manufact(cdi, s);
+		
+	default:
+		cdinfo(CD_WARNING, ": Invalid DVD structure read requested (%d)\n",
+					s->type);
+		return -EINVAL;
+	}
+}
+
+static int cdrom_mode_sense(struct cdrom_device_info *cdi,
+			    struct cdrom_generic_command *cgc,
+			    int page_code, int page_control)
+{
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(cgc->cmd, 0, sizeof(cgc->cmd));
+
+	cgc->cmd[0] = GPCMD_MODE_SENSE_10;
+	cgc->cmd[2] = page_code | (page_control << 6);
+	cgc->cmd[7] = cgc->buflen >> 8;
+	cgc->cmd[8] = cgc->buflen & 0xff;
+	return cdo->generic_packet(cdi, cgc);
+}
+
+static int cdrom_mode_select(struct cdrom_device_info *cdi,
+			     struct cdrom_generic_command *cgc)
+{
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(cgc->cmd, 0, sizeof(cgc->cmd));
+	
+	cgc->cmd[0] = GPCMD_MODE_SELECT_10;
+	cgc->cmd[1] = 0x10;		/* PF */
+	cgc->cmd[7] = cgc->buflen >> 8;
+	cgc->cmd[8] = cgc->buflen & 0xff;
+	return cdo->generic_packet(cdi, cgc);
+}
+
+static int cdrom_read_subchannel(struct cdrom_device_info *cdi,
+				 struct cdrom_subchnl *subchnl, int mcn)
+{
+	struct cdrom_device_ops *cdo = cdi->ops;
+	struct cdrom_generic_command cgc;
+	char buffer[32];
+	int ret;
+
+	memset(&cgc, 0, sizeof(cgc));
+	cgc.cmd[0] = GPCMD_READ_SUBCHANNEL;
+	cgc.cmd[1] = 2;     /* MSF addressing */
+	cgc.cmd[2] = 0x40;  /* request subQ data */
+	cgc.cmd[3] = mcn ? 2 : 1;
+	cgc.cmd[8] = cgc.buflen = 16;
+	cgc.buffer = buffer;
+
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		return ret;
+
+	subchnl->cdsc_audiostatus = cgc.buffer[1];
+	subchnl->cdsc_format = CDROM_MSF;
+	subchnl->cdsc_ctrl = cgc.buffer[5] & 0xf;
+	subchnl->cdsc_trk = cgc.buffer[6];
+	subchnl->cdsc_ind = cgc.buffer[7];
+
+	subchnl->cdsc_reladdr.msf.minute = cgc.buffer[13];
+	subchnl->cdsc_reladdr.msf.second = cgc.buffer[14];
+	subchnl->cdsc_reladdr.msf.frame = cgc.buffer[15];
+	subchnl->cdsc_absaddr.msf.minute = cgc.buffer[9];
+	subchnl->cdsc_absaddr.msf.second = cgc.buffer[10];
+	subchnl->cdsc_absaddr.msf.frame = cgc.buffer[11];
+
+	return 0;
+}
+
+/* very generic interface for reading the various types of blocks */
+static int cdrom_read_block(struct cdrom_device_info *cdi,
+			    struct cdrom_generic_command *cgc,
+			    int lba, int nblocks, int format, int blksize)
+{
+	struct cdrom_device_ops *cdo = cdi->ops;
+
+	memset(&cgc->cmd, 0, sizeof(cgc->cmd));
+	cgc->cmd[0] = GPCMD_READ_CD;
+	/* expected sector size - cdda,mode1,etc. */
+	cgc->cmd[1] = format << 2;
+	/* starting address */
+	cgc->cmd[2] = (lba >> 24) & 0xff;
+	cgc->cmd[3] = (lba >> 16) & 0xff;
+	cgc->cmd[4] = (lba >>  8) & 0xff;
+	cgc->cmd[5] = lba & 0xff;
+	/* number of blocks */
+	cgc->cmd[8] = nblocks;
+	cgc->buflen = blksize * nblocks;
+	switch (blksize) {
+	case CD_FRAMESIZE_RAW0	: cgc->cmd[9] = 0x58;
+	case CD_FRAMESIZE_RAW1	: cgc->cmd[9] = 0x78;
+	case CD_FRAMESIZE_RAW	: cgc->cmd[9] = 0xf8;
+	default			: cgc->cmd[9] = 0x10;
+	}
+	
+	return cdo->generic_packet(cdi, cgc);
+}
+
 /* Some of the cdrom ioctls are not implemented here, because these
  * appear to be either too device-specific, or it is not clear to me
  * what use they are. These are (number of drivers that support them
@@ -704,6 +1170,7 @@
 	kdev_t dev = ip->i_rdev;
 	struct cdrom_device_info *cdi = cdrom_find_device (dev);
 	struct cdrom_device_ops *cdo;
+	int ret;
 
 	if (cdi == NULL)
 		return -ENODEV;
@@ -715,7 +1182,6 @@
 		/* maybe we should order cases after statistics of use? */
 
 	case CDROMMULTISESSION: {
-		int ret;
 		struct cdrom_multisession ms_info;
 		u_char requested_format;
 		cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n"); 
@@ -737,28 +1203,28 @@
 		}
 
 	case CDROMEJECT: {
-		int ret;
 		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); 
-		if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY))
+		if (!CDROM_CAN(CDC_OPEN_TRAY))
 			return -ENOSYS;
 		if (cdi->use_count != 1 || keeplocked)
 			return -EBUSY;
-		if (cdo->capability & ~cdi->mask & CDC_LOCK)
+		if (CDROM_CAN(CDC_LOCK))
 			if ((ret=cdo->lock_door(cdi, 0)))
 				return ret;
 
 		return cdo->tray_move(cdi, 1);
 		}
 
-	case CDROMCLOSETRAY:
+	case CDROMCLOSETRAY: {
 		cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); 
-		if (!(cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY))
+		if (!CDROM_CAN(CDC_CLOSE_TRAY))
 			return -ENOSYS;
 		return cdo->tray_move(cdi, 0);
+		}
 
-	case CDROMEJECT_SW:
+	case CDROMEJECT_SW: {
 		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); 
-		if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY))
+		if (!CDROM_CAN(CDC_OPEN_TRAY))
 			return -ENOSYS;
 		if (keeplocked)
 			return -EBUSY;
@@ -766,13 +1232,13 @@
 		if (arg)
 			cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
 		return 0;
+		}
 
 	case CDROM_MEDIA_CHANGED: {
 		cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); 
-		if (!(cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED))
+		if (!CDROM_CAN(CDC_MEDIA_CHANGED))
 			return -ENOSYS;
-		if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
-		    || arg == CDSL_CURRENT)
+		if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
 			/* cannot select disc or select current disc */
 			return media_changed(cdi, 1);
 		if ((unsigned int)arg >= cdi->capacity)
@@ -780,7 +1246,7 @@
 		return cdo->media_changed (cdi, arg);
 		}
 
-	case CDROM_SET_OPTIONS:
+	case CDROM_SET_OPTIONS: {
 		cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); 
 		/* options need to be in sync with capability. too late for
 		   that, so we have to check each one separately... */
@@ -789,52 +1255,56 @@
 		case CDO_CHECK_TYPE:
 			break;
 		case CDO_LOCK:
-			if (!(cdo->capability & ~cdi->mask & CDC_LOCK))
+			if (!CDROM_CAN(CDC_LOCK))
 				return -ENOSYS;
 			break;
 		case 0:
 			return cdi->options;
 		/* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */
 		default:
-			if (!(cdo->capability & ~cdi->mask & arg))
+			if (!CDROM_CAN(arg))
 				return -ENOSYS;
 		}
 		cdi->options |= (int) arg;
 		return cdi->options;
+		}
 
-	case CDROM_CLEAR_OPTIONS:
+	case CDROM_CLEAR_OPTIONS: {
 		cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); 
 		cdi->options &= ~(int) arg;
 		return cdi->options;
+		}
 
 	case CDROM_SELECT_SPEED: {
 		cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n"); 
-		if (!(cdo->capability & ~cdi->mask & CDC_SELECT_SPEED))
+		if (!CDROM_CAN(CDC_SELECT_SPEED))
 			return -ENOSYS;
 		return cdo->select_speed(cdi, arg);
 		}
 
 	case CDROM_SELECT_DISC: {
 		cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); 
-		if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC))
+		if (!CDROM_CAN(CDC_SELECT_DISC))
 			return -ENOSYS;
                 if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) 
 			return cdo->select_disc(cdi, arg);
 		if ((int)arg >= cdi->capacity)
-			return -EDRIVE_CANT_DO_THIS;
+			return -EINVAL;
 		return cdo->select_disc(cdi, arg);
 		}
 
 	case CDROMRESET: {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
 		cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
-		if (!(cdo->capability & ~cdi->mask & CDC_RESET))
+		if (!CDROM_CAN(CDC_RESET))
 			return -ENOSYS;
 		return cdo->reset(cdi);
 		}
 
 	case CDROM_LOCKDOOR: {
-		cdinfo(CD_DO_IOCTL, "%socking door.\n",arg?"L":"Unl");
-		if (!(cdo->capability & ~cdi->mask & CDC_LOCK)) {
+		cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl");
+		if (!CDROM_CAN(CDC_LOCK)) {
 			return -EDRIVE_CANT_DO_THIS;
 		} else {
 			keeplocked = arg ? 1 : 0;
@@ -845,14 +1315,14 @@
 	case CDROM_DEBUG: {
 		if (!capable(CAP_SYS_ADMIN))
 			return -EACCES;
-		cdinfo(CD_DO_IOCTL, "%sabling debug.\n",arg?"En":"Dis");
+		cdinfo(CD_DO_IOCTL, "%sabling debug.\n", arg ? "En" : "Dis");
 		debug = arg ? 1 : 0;
 		return debug;
 		}
 
 	case CDROM_GET_CAPABILITY: {
 		cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
-		return cdo->capability;
+		return (cdo->capability & ~cdi->mask);
 		}
 
 /* The following function is implemented, although very few audio
@@ -861,7 +1331,6 @@
  * is written on the CD is /not/ uniform across all discs!
  */
 	case CDROM_GET_MCN: {
-		int ret;
 		struct cdrom_mcn mcn;
 		cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n"); 
 		if (!(cdo->capability & CDC_MCN))
@@ -877,9 +1346,11 @@
 		cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); 
 		if (!(cdo->capability & CDC_DRIVE_STATUS))
 			return -ENOSYS;
+		if (!CDROM_CAN(CDC_SELECT_DISC))
+			return cdo->drive_status(cdi, CDSL_CURRENT);
                 if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) 
-			return cdo->drive_status(cdi, arg);
-                if (((int)arg > cdi->capacity))
+			return cdo->drive_status(cdi, CDSL_CURRENT);
+		if (((int)arg >= cdi->capacity))
 			return -EINVAL;
 		return cdo->drive_status(cdi, arg);
 		}
@@ -923,146 +1394,561 @@
 		return CDS_NO_INFO;
 		}
 
-	case CDROM_CHANGER_NSLOTS:
+	case CDROM_CHANGER_NSLOTS: {
 		cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); 
-	return cdi->capacity;
+		return cdi->capacity;
+		}
+	}
 
-/* The following is not implemented, because there are too many
- * different data types. We could support /1/ raw mode, that is large
- * enough to hold everything.
- */
+	/* use the ioctls that are implemented through the generic_packet()
+	   interface. this may look at bit funny, but if -ENOTTY is
+	   returned that particular ioctl is not implemented and we
+	   let it go through the device specific ones. */
+	if (CDROM_CAN(CDC_GENERIC_PACKET)) {
+		ret = mmc_ioctl(cdi, cmd, arg);
+		if (ret != -ENOTTY) {
+			return ret;
+		}
+	}
 
-#if 0
-	case CDROMREADMODE1: {
-		int ret;
+	/* note: most of the cdinfo() calls are commented out here,
+	   because they fill up the sys log when CD players poll
+	   the drive. */
+	switch (cmd) {
+	case CDROMSUBCHNL: {
+		struct cdrom_subchnl q;
+		u_char requested, back;
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ 
+		IOCTL_IN(arg, struct cdrom_subchnl, q);
+		requested = q.cdsc_format;
+		if (!((requested == CDROM_MSF) ||
+		      (requested == CDROM_LBA)))
+			return -EINVAL;
+		q.cdsc_format = CDROM_MSF;
+		if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))
+			return ret;
+		back = q.cdsc_format; /* local copy */
+		sanitize_format(&q.cdsc_absaddr, &back, requested);
+		sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
+		IOCTL_OUT(arg, struct cdrom_subchnl, q);
+		/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
+		return 0;
+		}
+	case CDROMREADTOCHDR: {
+		struct cdrom_tochdr header;
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ 
+		IOCTL_IN(arg, struct cdrom_tochdr, header);
+		if ((ret=cdo->audio_ioctl(cdi, cmd, &header)))
+			return ret;
+		IOCTL_OUT(arg, struct cdrom_tochdr, header);
+		/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */ 
+		return 0;
+		}
+	case CDROMREADTOCENTRY: {
+		struct cdrom_tocentry entry;
+		u_char requested_format;
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ 
+		IOCTL_IN(arg, struct cdrom_tocentry, entry);
+		requested_format = entry.cdte_format;
+		if (!((requested_format == CDROM_MSF) || 
+			(requested_format == CDROM_LBA)))
+				return -EINVAL;
+		/* make interface to low-level uniform */
+		entry.cdte_format = CDROM_MSF;
+		if ((ret=cdo->audio_ioctl(cdi, cmd, &entry)))
+			return ret;
+		sanitize_format(&entry.cdte_addr,
+		&entry.cdte_format, requested_format);
+		IOCTL_OUT(arg, struct cdrom_tocentry, entry);
+		/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */ 
+		return 0;
+		}
+	case CDROMPLAYMSF: {
 		struct cdrom_msf msf;
-		char buf[CD_FRAMESIZE];
-		cdinfo(CD_DO_IOCTL, "entering CDROMREADMODE1\n"); 
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); 
 		IOCTL_IN(arg, struct cdrom_msf, msf);
-		if (ret=cdo->read_audio(dev, cmd, &msf, &buf, cdi))
+		return cdo->audio_ioctl(cdi, cmd, &msf);
+		}
+	case CDROMPLAYTRKIND: {
+		struct cdrom_ti ti;
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); 
+		IOCTL_IN(arg, struct cdrom_ti, ti);
+		CHECKAUDIO;
+		return cdo->audio_ioctl(cdi, cmd, &ti);
+		}
+	case CDROMVOLCTRL: {
+		struct cdrom_volctrl volume;
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); 
+		IOCTL_IN(arg, struct cdrom_volctrl, volume);
+		return cdo->audio_ioctl(cdi, cmd, &volume);
+		}
+	case CDROMVOLREAD: {
+		struct cdrom_volctrl volume;
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); 
+		if ((ret=cdo->audio_ioctl(cdi, cmd, &volume)))
 			return ret;
-		IOCTL_OUT(arg, __typeof__(buf), buf);
+		IOCTL_OUT(arg, struct cdrom_volctrl, volume);
 		return 0;
 		}
-#endif
+	case CDROMSTART:
+	case CDROMSTOP:
+	case CDROMPAUSE:
+	case CDROMRESUME: {
+		if (!CDROM_CAN(CDC_PLAY_AUDIO))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); 
+		CHECKAUDIO;
+		return cdo->audio_ioctl(cdi, cmd, NULL);
+		}
 	} /* switch */
 
-/* Now all the audio-ioctls follow, they are all routed through the
-   same call audio_ioctl(). */
+	/* do the device specific ioctls */
+	if (CDROM_CAN(CDC_IOCTLS))
+		return cdo->dev_ioctl(cdi, cmd, arg);
+	
+	return -ENOSYS;
+}
+
+static inline
+int msf_to_lba (char m, char s, char f)
+{
+	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
+}
 
-#define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret
+static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
+		     unsigned long arg)
+{		
+	struct cdrom_device_ops *cdo = cdi->ops;
+	kdev_t dev = cdi->dev;
+	struct cdrom_generic_command cgc;
+	char buffer[32];
+	int ret = 0;
 
-	if (!(cdo->capability & CDC_PLAY_AUDIO))
-		return -ENOSYS;
-	else {
+	memset(&cgc, 0, sizeof(cgc));
+
+	/* build a unified command and queue it through
+	   cdo->generic_packet() */
+	switch (cmd) {
+	case CDROMREADRAW:
+	case CDROMREADMODE1:
+	case CDROMREADMODE2: {
+		struct cdrom_msf msf;
+		int blocksize = 0, format = 0, lba;
+		
 		switch (cmd) {
-		case CDROMSUBCHNL: {
-			int ret;
-			struct cdrom_subchnl q;
-			u_char requested, back;
-			/* comment out the cdinfo calls here because they
-			   fill up the sys logs when CD players poll the drive*/
-			/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ 
-			IOCTL_IN(arg, struct cdrom_subchnl, q);
-			requested = q.cdsc_format;
-                        if (!((requested == CDROM_MSF) ||
-                                (requested == CDROM_LBA)))
-                                        return -EINVAL;
-			q.cdsc_format = CDROM_MSF;
-			if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))
-				return ret;
-			back = q.cdsc_format; /* local copy */
-			sanitize_format(&q.cdsc_absaddr, &back, requested);
-			sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
-			IOCTL_OUT(arg, struct cdrom_subchnl, q);
-			/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
-			return 0;
-			}
-		case CDROMREADTOCHDR: {
-			int ret;
-			struct cdrom_tochdr header;
-			/* comment out the cdinfo calls here because they
-			   fill up the sys logs when CD players poll the drive*/
-			/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ 
-			IOCTL_IN(arg, struct cdrom_tochdr, header);
-			if ((ret=cdo->audio_ioctl(cdi, cmd, &header)))
-				return ret;
-			IOCTL_OUT(arg, struct cdrom_tochdr, header);
-			/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */ 
-			return 0;
-			}
-		case CDROMREADTOCENTRY: {
-			int ret;
-			struct cdrom_tocentry entry;
-			u_char requested_format;
-			/* comment out the cdinfo calls here because they
-			   fill up the sys logs when CD players poll the drive*/
-			/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ 
-			IOCTL_IN(arg, struct cdrom_tocentry, entry);
-			requested_format = entry.cdte_format;
-			if (!((requested_format == CDROM_MSF) || 
-				(requested_format == CDROM_LBA)))
-					return -EINVAL;
-			/* make interface to low-level uniform */
-			entry.cdte_format = CDROM_MSF;
-			if ((ret=cdo->audio_ioctl(cdi, cmd, &entry)))
-				return ret;
-			sanitize_format(&entry.cdte_addr,
-			&entry.cdte_format, requested_format);
-			IOCTL_OUT(arg, struct cdrom_tocentry, entry);
-			/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */ 
+		case CDROMREADRAW:
+			blocksize = CD_FRAMESIZE_RAW;
+			format = 0;
+			break;
+		case CDROMREADMODE1:
+			blocksize = CD_FRAMESIZE; break;
+			format = 2;
+			break;
+		case CDROMREADMODE2:
+			blocksize = CD_FRAMESIZE_RAW0; break;
+			format = 0;
+			break;
+		}
+		IOCTL_IN(arg, struct cdrom_msf, msf);
+		lba = msf_to_lba(msf.cdmsf_min0,msf.cdmsf_sec0,msf.cdmsf_frame0);
+		/* FIXME: we need upper bound checking, too!! */
+		if (lba < 0)
+			return -EINVAL;
+		cgc.buffer = (char *) kmalloc(blocksize, GFP_KERNEL);
+		if (cgc.buffer == NULL)
+			return -ENOMEM;
+		ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize);
+		if (!ret)
+			if (copy_to_user((char *)arg, cgc.buffer, blocksize))
+				return -EFAULT;
+		kfree(cgc.buffer);
+		return ret;
+		}
+	case CDROMREADAUDIO: {
+		struct cdrom_read_audio ra;
+		int lba;
+
+		IOCTL_IN(arg, struct cdrom_read_audio, ra);
+
+		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;
+
+		/* FIXME: we need upper bound checking, too!! */
+		if (lba < 0)
+			return -EINVAL;
+
+		/* do between 1 and 8 frames at the time */
+		if (ra.nframes > 8 || ra.nframes < 1)
+			return -EINVAL;
+
+		/* just a nice round figure */
+		if ((cgc.buffer = (char *) kmalloc(CD_FRAMESIZE_RAW*ra.nframes,
+						   GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+
+		if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW))
+			return -EFAULT;
+
+		while (ra.nframes > 0) {
+			ret = cdrom_read_block(cdi, &cgc, lba, ra.nframes, 1,
+					       CD_FRAMESIZE_RAW);
+			if (ret) break;
+			__copy_to_user(ra.buf, cgc.buffer,
+				       CD_FRAMESIZE_RAW*ra.nframes);
+			ra.buf += (CD_FRAMESIZE_RAW * ra.nframes);
+			ra.nframes -= ra.nframes;
+			lba += ra.nframes;
+		}
+		kfree(cgc.buffer);
+		return ret;
+		}
+	case CDROMSUBCHNL: {
+		struct cdrom_subchnl q;
+		u_char requested, back;
+		IOCTL_IN(arg, struct cdrom_subchnl, q);
+		requested = q.cdsc_format;
+		if (!((requested == CDROM_MSF) ||
+		      (requested == CDROM_LBA)))
+			return -EINVAL;
+		q.cdsc_format = CDROM_MSF;
+		if ((ret = cdrom_read_subchannel(cdi, &q, 0)))
+			return ret;
+		back = q.cdsc_format; /* local copy */
+		sanitize_format(&q.cdsc_absaddr, &back, requested);
+		sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
+		IOCTL_OUT(arg, struct cdrom_subchnl, q);
+		/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
+		return 0;
+		}
+	case CDROMPLAYTRKIND: {
+		struct cdrom_ti ti;
+		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n");
+		IOCTL_IN(arg, struct cdrom_ti, ti);
+		cgc.cmd[0] = GPCMD_PLAYAUDIO_TI;
+		cgc.cmd[4] = ti.cdti_trk0;
+		cgc.cmd[5] = ti.cdti_ind0;
+		cgc.cmd[7] = ti.cdti_trk1;
+		cgc.cmd[8] = ti.cdti_ind1;
+		return cdo->generic_packet(cdi, &cgc);
+		}
+	case CDROMPLAYMSF: {
+		struct cdrom_msf msf;
+		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
+		IOCTL_IN(arg, struct cdrom_msf, msf);
+		cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+		cgc.cmd[3] = msf.cdmsf_min0;
+		cgc.cmd[4] = msf.cdmsf_sec0;
+		cgc.cmd[5] = msf.cdmsf_frame0;
+		cgc.cmd[6] = msf.cdmsf_min1;
+		cgc.cmd[7] = msf.cdmsf_sec1;
+		cgc.cmd[8] = msf.cdmsf_frame1;
+		return cdo->generic_packet(cdi, &cgc);
+		}
+	case CDROMPLAYBLK: {
+		struct cdrom_blk blk;
+		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
+		IOCTL_IN(arg, struct cdrom_blk, blk);
+		cgc.cmd[0] = GPCMD_PLAY_AUDIO_10;
+		cgc.cmd[2] = blk.from >> 24;
+		cgc.cmd[3] = blk.from >> 16;
+		cgc.cmd[4] = blk.from >> 8;
+		cgc.cmd[5] = blk.from;
+		cgc.cmd[7] = blk.len >> 8;
+		cgc.cmd[8] = blk.len;
+		return cdo->generic_packet(cdi, &cgc);
+		}
+	case CDROMVOLCTRL:
+	case CDROMVOLREAD: {
+		struct cdrom_volctrl volctrl;
+		char mask[32];
+		unsigned short offset;
+		cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");
+
+		IOCTL_IN(arg, struct cdrom_volctrl, volctrl);
+
+		cgc.buffer = buffer;
+		cgc.buflen = 24;
+		if ((ret = cdrom_mode_sense(cdi, &cgc, 
+				GPMODE_AUDIO_CTL_PAGE, 0)))
+		    return ret;
+		
+		/* some drives have longer pages, adjust and reread. */
+		if (buffer[1] > cgc.buflen) {
+			cgc.buflen = buffer[1] + 2;
+			if ((ret = cdrom_mode_sense(cdi, &cgc, 
+					GPMODE_AUDIO_CTL_PAGE, 0))) 
+			    return ret;
+		}
+		
+		/* get the offset from the length of the page. length
+		   is measure from byte 2 an on, thus the 14. */
+		offset = buffer[1] - 14;
+
+		/* now we have the current volume settings. if it was only
+		   a CDROMVOLREAD, return these values */
+		if (cmd == CDROMVOLREAD) {
+			volctrl.channel0 = buffer[offset+9];
+			volctrl.channel1 = buffer[offset+11];
+			volctrl.channel2 = buffer[offset+13];
+			volctrl.channel3 = buffer[offset+15];
+			IOCTL_OUT(arg, struct cdrom_volctrl, volctrl);
 			return 0;
-			}
-		case CDROMPLAYMSF: {
-			int ret;
-			struct cdrom_msf msf;
-			cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); 
-			IOCTL_IN(arg, struct cdrom_msf, msf);
-			CHECKAUDIO;
-			return cdo->audio_ioctl(cdi, cmd, &msf);
-			}
-		case CDROMPLAYTRKIND: {
-			int ret;
-			struct cdrom_ti ti;
-			cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); 
-			IOCTL_IN(arg, struct cdrom_ti, ti);
-			CHECKAUDIO;
-			return cdo->audio_ioctl(cdi, cmd, &ti);
-			}
-		case CDROMVOLCTRL: {
-			struct cdrom_volctrl volume;
-			cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); 
-			IOCTL_IN(arg, struct cdrom_volctrl, volume);
-			return cdo->audio_ioctl(cdi, cmd, &volume);
-			}
-		case CDROMVOLREAD: {
-			int ret;
-			struct cdrom_volctrl volume;
-			cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); 
-			if ((ret=cdo->audio_ioctl(cdi, cmd, &volume)))
-				return ret;
-			IOCTL_OUT(arg, struct cdrom_volctrl, volume);
+		}
+		
+		/* get the volume mask */
+		cgc.buffer = mask;
+		if ((ret = cdrom_mode_sense(cdi, &cgc, 
+				GPMODE_AUDIO_CTL_PAGE, 1)))
+		    return ret;
+
+		buffer[offset+9] = volctrl.channel0 & mask[offset+9];
+		buffer[offset+11] = volctrl.channel1 & mask[offset+11];
+		buffer[offset+13] = volctrl.channel2 & mask[offset+13];
+		buffer[offset+15] = volctrl.channel3 & mask[offset+15];
+
+		/* clear the first three */
+		memset(buffer, 0, 3);
+
+		/* set volume */
+		cgc.buffer = buffer;
+		return cdrom_mode_select(cdi, &cgc);
+		}
+
+	case CDROMSTART:
+	case CDROMSTOP: {
+		cdinfo(CD_DO_IOCTL, "entering audio ioctl (start/stop)\n"); 
+		cgc.cmd[0] = GPCMD_START_STOP_UNIT;
+		cgc.cmd[1] = 1;
+		cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0;
+		return cdo->generic_packet(cdi, &cgc);
+		}
+
+	case CDROMPAUSE:
+	case CDROMRESUME: {
+		cdinfo(CD_DO_IOCTL, "entering audio ioctl (pause/resume)\n"); 
+		cgc.cmd[0] = GPCMD_PAUSE_RESUME;
+		cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0;
+		return cdo->generic_packet(cdi, &cgc);
+		}
+	
+	case DVD_READ_STRUCT: {
+		dvd_struct s;
+		if (!CDROM_CAN(CDC_DVD))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "entering dvd_read_struct\n"); 
+		IOCTL_IN(arg, dvd_struct, s);
+		if ((ret = dvd_read_struct(cdi, &s)) == 0) {
+			IOCTL_OUT(arg, dvd_struct, s);
 			return 0;
-			}
-		case CDROMSTART:
-		case CDROMSTOP:
-		case CDROMPAUSE:
-		case CDROMRESUME: {
-			int ret;
-			cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); 
-			CHECKAUDIO;
-			return cdo->audio_ioctl(cdi, cmd, NULL);
-			}
-		} /* switch */
+		}
+		return ret;
+		}
+
+	case DVD_AUTH: {
+		dvd_authinfo ai;
+		if (!CDROM_CAN(CDC_DVD))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "entering dvd_auth\n"); 
+		IOCTL_IN(arg, dvd_authinfo, ai);
+		if ((ret = dvd_do_auth (cdi, &ai)))
+			return ret;
+		IOCTL_OUT(arg, dvd_authinfo, ai);
+		return 0;
+		}
+
+	case CDROM_SEND_PACKET: {
+		if (!CDROM_CAN(CDC_GENERIC_PACKET))
+			return -ENOSYS;
+		cdinfo(CD_DO_IOCTL, "entering send_packet\n"); 
+		IOCTL_IN(arg, struct cdrom_generic_command, cgc);
+		cgc.buffer = kmalloc(cgc.buflen, GFP_KERNEL);
+		ret = cdo->generic_packet(cdi, &cgc);
+		if (copy_to_user((void*)arg, cgc.buffer, cgc.buflen)) {
+			kfree(cgc.buffer);
+			return -EFAULT;
+		}
+		kfree(cgc.buffer);
+		return ret;
+		}
+	case CDROM_NEXT_WRITABLE: {
+		long next;
+		if ((ret = cdrom_get_next_writable(dev, &next)))
+			return ret;
+		IOCTL_OUT(arg, long, next);
+		return 0;
+		}
+	case CDROM_LAST_WRITTEN: {
+		long last;
+		if ((ret = cdrom_get_last_written(dev, &last)))
+			return ret;
+		IOCTL_OUT(arg, long, last);
+		return 0;
+		}
+	} /* switch */
+
+	return -ENOTTY;
+}
+
+static int cdrom_get_track_info(kdev_t dev, __u16 track, __u8 type,
+				 track_information *ti)
+{
+        struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_ops *cdo = cdi->ops;
+	struct cdrom_generic_command cgc;
+	int ret;
+
+	memset(&cgc, 0, sizeof(cgc));
+	cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
+	cgc.cmd[1] = type & 3;
+	cgc.cmd[4] = (track & 0xff00) >> 8;
+	cgc.cmd[5] = track & 0xff;
+	cgc.cmd[8] = cgc.buflen = 8;
+	cgc.buffer = (char *)ti;
+
+	ret = cdo->generic_packet(cdi, &cgc);
+	if (ret) return ret;
+	
+	cgc.cmd[8] = cgc.buflen = be16_to_cpu(ti->track_information_length) +
+		     sizeof(ti->track_information_length);
+	return cdo->generic_packet(cdi, &cgc);
+}
+
+static int cdrom_get_disc_info(kdev_t dev, disc_information *di)
+{
+	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_ops *cdo = cdi->ops;
+	struct cdrom_generic_command cgc;
+
+	/* set up command and get the disc info */
+	memset(&cgc, 0, sizeof(cgc));
+	memset(di, 0, sizeof(disc_information));
+	cgc.cmd[0] = GPCMD_READ_DISC_INFO;
+	cgc.cmd[8] = cgc.buflen = sizeof(*di);
+	cgc.buffer = (char *)di;
+
+	return cdo->generic_packet(cdi, &cgc);
+}
+
+
+/* return the last written block on the CD-R media. this is for the udf
+   file system. */
+int cdrom_get_last_written(kdev_t dev, long *last_written)
+{	
+	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_tocentry toc;
+	disc_information di;
+	track_information ti;
+	__u32 last_track;
+	int ret = -1;
+	
+	if (!CDROM_CAN(CDC_GENERIC_PACKET))
+		goto use_toc;
+	
+	if ((ret = cdrom_get_disc_info(dev, &di)))
+		goto use_toc;
+
+	last_track = (di.last_track_msb << 8) | di.last_track_lsb;
+	if ((ret = cdrom_get_track_info(dev, last_track, 1, &ti)))
+		goto use_toc;
+
+	/* if this track is blank, try the previous. */
+	if (ti.blank) {
+		last_track--;
+		if ((ret = cdrom_get_track_info(dev, last_track, 1, &ti)))
+			goto use_toc;
 	}
 
-	/* device specific ioctls? */
-	if (!(cdo->capability & CDC_IOCTLS))
-		return -ENOSYS;
-	else 
-		return cdo->dev_ioctl(cdi, cmd, arg);
+	/* if last recorded field is valid, return it. */
+	if (ti.lra_v) {
+		*last_written = be32_to_cpu(ti.last_rec_address);
+	} else {
+		/* make it up instead */
+		*last_written = be32_to_cpu(ti.track_start) +
+				be32_to_cpu(ti.track_size);
+		if (ti.free_blocks)
+			*last_written -= (be32_to_cpu(ti.free_blocks) + 7);
+	}
+	return 0;
+
+	/* this is where we end up if the drive either can't do a
+	   GPCMD_READ_DISC_INFO or GPCMD_READ_TRACK_RZONE_INFO or if
+	   it fails. then we return the toc contents. */
+use_toc:
+	toc.cdte_format = CDROM_MSF;
+	toc.cdte_track = CDROM_LEADOUT;
+	if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc))
+		return ret;
+	sanitize_format(&toc.cdte_addr, &toc.cdte_format, CDROM_LBA);
+	*last_written = toc.cdte_addr.lba;
+	return 0;
 }
 
+/* return the next writable block. also for udf file system. */
+int cdrom_get_next_writable(kdev_t dev, long *next_writable)
+{
+	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	disc_information di;
+	track_information ti;
+	__u16 last_track;
+	int ret = -1;
+	
+	if (!CDROM_CAN(CDC_GENERIC_PACKET))
+		goto use_last_written;
+
+	if ((ret = cdrom_get_disc_info(dev, &di)))
+		goto use_last_written;
+
+	last_track = (di.last_track_msb << 8) | di.last_track_lsb;
+	if ((ret = cdrom_get_track_info(dev, last_track, 1, &ti)))
+		goto use_last_written;
+
+        /* if this track is blank, try the previous. */
+	if (ti.blank) {
+		last_track--;
+		if ((ret = cdrom_get_track_info(dev, last_track, 1, &ti)))
+			goto use_last_written;
+	}
+
+	/* if next recordable address field is valid, use it. */
+	if (ti.nwa_v)
+		*next_writable = be32_to_cpu(ti.next_writable);
+	else
+		goto use_last_written;
+
+	return 0;
+
+use_last_written:
+	if ((ret = cdrom_get_last_written(dev, next_writable))) {
+		*next_writable = 0;
+		return ret;
+	} else {
+		*next_writable += 7;
+		return 0;
+	}
+}
+
+EXPORT_SYMBOL(cdrom_get_next_writable);
+EXPORT_SYMBOL(cdrom_get_last_written);
 EXPORT_SYMBOL(cdrom_count_tracks);
 EXPORT_SYMBOL(register_cdrom);
 EXPORT_SYMBOL(unregister_cdrom);
@@ -1072,90 +1958,197 @@
 
 #define CDROM_STR_SIZE 1000
 
-static char cdrom_drive_info[CDROM_STR_SIZE]="info\n";
+struct cdrom_sysctl_settings {
+	char	info[CDROM_STR_SIZE];	/* general info */
+	int	autoclose;		/* close tray upon mount, etc */
+	int	autoeject;		/* eject on umount */
+	int	debug;			/* turn on debugging messages */
+	int	lock;			/* lock the door on device open */
+	int	check;			/* check media type */
+} cdrom_sysctl_settings;
 
 int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
                            void *buffer, size_t *lenp)
 {
         int pos;
 	struct cdrom_device_info *cdi;
+	char *info = cdrom_sysctl_settings.info;
 	
 	if (!*lenp || (filp->f_pos && !write)) {
 		*lenp = 0;
 		return 0;
 	}
 
-	pos = sprintf(cdrom_drive_info, "CD-ROM information, " VERSION "\n");
+	pos = sprintf(info, "CD-ROM information, " VERSION "\n");
 	
-	pos += sprintf(cdrom_drive_info+pos, "\ndrive name:\t");
+	pos += sprintf(info+pos, "\ndrive name:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%s", cdi->name);
+	    pos += sprintf(info+pos, "\t%s", cdi->name);
 
-	pos += sprintf(cdrom_drive_info+pos, "\ndrive speed:\t");
+	pos += sprintf(info+pos, "\ndrive speed:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d", cdi->speed);
+	    pos += sprintf(info+pos, "\t%d", cdi->speed);
 
-	pos += sprintf(cdrom_drive_info+pos, "\ndrive # of slots:");
+	pos += sprintf(info+pos, "\ndrive # of slots:");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d", cdi->capacity);
+	    pos += sprintf(info+pos, "\t%d", cdi->capacity);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan close tray:\t");
+	pos += sprintf(info+pos, "\nCan close tray:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-		((cdi->ops->capability & ~cdi->mask & CDC_CLOSE_TRAY)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CLOSE_TRAY) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan open tray:\t");
+	pos += sprintf(info+pos, "\nCan open tray:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-		   ((cdi->ops->capability & ~cdi->mask & CDC_OPEN_TRAY)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_OPEN_TRAY) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan lock tray:\t");
+	pos += sprintf(info+pos, "\nCan lock tray:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-		   ((cdi->ops->capability & ~cdi->mask & CDC_LOCK)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_LOCK) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan change speed:");
+	pos += sprintf(info+pos, "\nCan change speed:");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-		   ((cdi->ops->capability & ~cdi->mask & CDC_SELECT_SPEED)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_SPEED) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan select disk:");
+	pos += sprintf(info+pos, "\nCan select disk:");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-		   ((cdi->ops->capability & ~cdi->mask & CDC_SELECT_DISC)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_DISC) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan read multisession:");
+	pos += sprintf(info+pos, "\nCan read multisession:");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-			   ((cdi->ops->capability & CDC_MULTI_SESSION)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MULTI_SESSION) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan read MCN:\t");
+	pos += sprintf(info+pos, "\nCan read MCN:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-			   ((cdi->ops->capability & CDC_MCN)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MCN) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nReports media changed:");
+	pos += sprintf(info+pos, "\nReports media changed:");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-			   ((cdi->ops->capability & CDC_MEDIA_CHANGED)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MEDIA_CHANGED) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan play audio:\t");
+	pos += sprintf(info+pos, "\nCan play audio:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
-		   ((cdi->ops->capability & ~cdi->mask & CDC_PLAY_AUDIO)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_PLAY_AUDIO) != 0);
 
-        strcpy(cdrom_drive_info+pos,"\n\n");
-	pos += 3;
-	if (*lenp > pos)
-		*lenp = pos;
+	pos += sprintf(info+pos, "\nCan write CD-R:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CD_R) != 0);
+
+	pos += sprintf(info+pos, "\nCan write CD-RW:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CD_RW) != 0);
+
+	pos += sprintf(info+pos, "\nCan read DVD:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD) != 0);
+
+	pos += sprintf(info+pos, "\nCan write DVD-R:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_R) != 0);
+
+	pos += sprintf(info+pos, "\nCan write DVD-RAM:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_RAM) != 0);
+
+	strcpy(info+pos,"\n\n");
 		
         return proc_dostring(ctl, write, filp, buffer, lenp);
 }
 
+/* Unfortunately, per device settings are not implemented through
+   procfs/sysctl yet. When they are, this will naturally disappear. For now
+   just update all drives. Later this will become the template on which
+   new registered drives will be based. */
+void cdrom_update_settings(void)
+{
+	struct cdrom_device_info *cdi;
+
+	for (cdi = topCdromPtr; cdi != NULL; cdi = cdi->next) {
+		if (autoclose && CDROM_CAN(CDC_CLOSE_TRAY))
+			cdi->options |= CDO_AUTO_CLOSE;
+		else if (!autoclose)
+			cdi->options &= ~CDO_AUTO_CLOSE;
+		if (autoeject && CDROM_CAN(CDC_OPEN_TRAY))
+			cdi->options |= CDO_AUTO_EJECT;
+		else if (!autoeject)
+			cdi->options &= ~CDO_AUTO_EJECT;
+		if (lockdoor && CDROM_CAN(CDC_LOCK))
+			cdi->options |= CDO_LOCK;
+		else if (!lockdoor)
+			cdi->options &= ~CDO_LOCK;
+		if (check_media_type)
+			cdi->options |= CDO_CHECK_TYPE;
+		else
+			cdi->options &= ~CDO_CHECK_TYPE;
+	}
+}
+
+static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp,
+				void *buffer, size_t *lenp)
+{
+	int *valp = ctl->data;
+	int val = *valp;
+	int ret;
+	
+	ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+	if (write && *valp != val) {
+	
+		/* we only care for 1 or 0. */
+		if (*valp)
+			*valp = 1;
+		else
+			*valp = 0;
+
+		switch (ctl->ctl_name) {
+		case DEV_CDROM_AUTOCLOSE: {
+			if (valp == &cdrom_sysctl_settings.autoclose)
+				autoclose = cdrom_sysctl_settings.autoclose;
+			break;
+			}
+		case DEV_CDROM_AUTOEJECT: {
+			if (valp == &cdrom_sysctl_settings.autoeject)
+				autoeject = cdrom_sysctl_settings.autoeject;
+			break;
+			}
+		case DEV_CDROM_DEBUG: {
+			if (valp == &cdrom_sysctl_settings.debug)
+				debug = cdrom_sysctl_settings.debug;
+			break;
+			}
+		case DEV_CDROM_LOCK: {
+			if (valp == &cdrom_sysctl_settings.lock)
+				lockdoor = cdrom_sysctl_settings.lock;
+			break;
+			}
+		case DEV_CDROM_CHECK_MEDIA: {
+			if (valp == &cdrom_sysctl_settings.check)
+				check_media_type = cdrom_sysctl_settings.check;
+			break;
+			}
+		}
+		/* update the option flags according to the changes. we
+		   don't have per device options through sysctl yet,
+		   but we will have and then this will disappear. */
+		cdrom_update_settings();
+	}
+
+        return ret;
+}
+
 /* Place files in /proc/sys/dev/cdrom */
 ctl_table cdrom_table[] = {
-	{DEV_CDROM_INFO, "info", &cdrom_drive_info, 
+	{DEV_CDROM_INFO, "info", &cdrom_sysctl_settings.info, 
 		CDROM_STR_SIZE, 0444, NULL, &cdrom_sysctl_info},
+	{DEV_CDROM_AUTOCLOSE, "autoclose", &cdrom_sysctl_settings.autoclose,
+		sizeof(int), 0644, NULL, &cdrom_sysctl_handler },
+	{DEV_CDROM_AUTOEJECT, "autoeject", &cdrom_sysctl_settings.autoeject,
+		sizeof(int), 0644, NULL, &cdrom_sysctl_handler },
+	{DEV_CDROM_DEBUG, "debug", &cdrom_sysctl_settings.debug,
+		sizeof(int), 0644, NULL, &cdrom_sysctl_handler },
+	{DEV_CDROM_LOCK, "lock", &cdrom_sysctl_settings.lock,
+		sizeof(int), 0644, NULL, &cdrom_sysctl_handler },
+	{DEV_CDROM_CHECK_MEDIA, "check_media", &cdrom_sysctl_settings.check,
+		sizeof(int), 0644, NULL, &cdrom_sysctl_handler },
 	{0}
 	};
 
@@ -1199,6 +2192,13 @@
 	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 1);
 	cdrom_root_table->child->de->fill_inode = &cdrom_procfs_modcount;
 
+	/* set the defaults */
+	cdrom_sysctl_settings.autoclose = autoclose;
+	cdrom_sysctl_settings.autoeject = autoeject;
+	cdrom_sysctl_settings.debug = debug;
+	cdrom_sysctl_settings.lock = lockdoor;
+	cdrom_sysctl_settings.check = check_media_type;
+
 	initialized = 1;
 }
 
@@ -1229,12 +2229,3 @@
 }
 
 #endif /* endif MODULE */
-
-
-
-/*
- * Local variables:
- * comment-column: 40
- * 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:
- */
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- /tmp/linux-2.2.12/drivers/scsi/sr.c	Mon Aug 23 13:32:23 1999
+++ linux/drivers/scsi/sr.c	Thu Sep  2 22:48:19 1999
@@ -20,6 +20,9 @@
  *          Modified by Gerd Knorr <kraxel@cs.tu-berlin.de> to support the
  *          generic cdrom interface
  *
+ *	 Modified by Jens Axboe <axboe@image.dk> - Uniform sr_packet()
+ *	 interface, capabilities probe additions, ioctl cleanups, etc.
+ *
  */
 
 #include <linux/module.h>
@@ -34,6 +37,7 @@
 #include <linux/interrupt.h>
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/uaccess.h>
 
 #define MAJOR_NR SCSI_CDROM_MAJOR
 #include <linux/blk.h>
@@ -70,6 +74,7 @@
 
 void requeue_sr_request (Scsi_Cmnd * SCpnt);
 static int sr_media_change(struct cdrom_device_info*, int);
+static int sr_packet(struct cdrom_device_info *, struct cdrom_generic_command *);
 
 static void sr_release(struct cdrom_device_info *cdi)
 {
@@ -99,8 +104,10 @@
         sr_dev_ioctl,                 /* device-specific ioctl */
         CDC_CLOSE_TRAY | CDC_OPEN_TRAY| CDC_LOCK | CDC_SELECT_SPEED |
         CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO |
-        CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS,
-        0
+        CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_CD_R | CDC_CD_RW |
+	CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_GENERIC_PACKET,
+        0,
+	sr_packet
 };
 
 /*
@@ -921,10 +928,14 @@
 	scsi_CDs[i].sector_size = 2048;  /* A guess, just in case */
 	scsi_CDs[i].needs_sector_size = 1;
     } else {
-	scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) |
-				    (buffer[1] << 16) |
-				    (buffer[2] << 8) |
-				    buffer[3]);
+#if 0
+	if (cdrom_get_last_written(MKDEV(MAJOR_NR, i),
+	   (long*)&scsi_CDs[i].capacity))
+#endif
+		scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) |
+					    (buffer[1] << 16) |
+					    (buffer[2] << 8) |
+					     buffer[3]);
 	scsi_CDs[i].sector_size = (buffer[4] << 24) |
 	    (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
 	switch (scsi_CDs[i].sector_size) {
@@ -988,25 +999,85 @@
         scsi_CDs[i].cdi.speed      = 1;
         /* disable speed select, drive probably can't do this either */
         scsi_CDs[i].cdi.mask      |= CDC_SELECT_SPEED;
-    } else {
-        n = buffer[3]+4;
-        scsi_CDs[i].cdi.speed    = ((buffer[n+8] << 8) + buffer[n+9])/176;
-      	scsi_CDs[i].readcd_known = 1;
-        scsi_CDs[i].readcd_cdda  = buffer[n+5] & 0x01;
-        /* print some capability bits */
-        printk("sr%i: scsi3-mmc drive: %dx/%dx %s%s%s%s%s\n",i,
-               ((buffer[n+14] << 8) + buffer[n+15])/176,
-               scsi_CDs[i].cdi.speed,
-               buffer[n+3]&0x01 ? "writer " : "",   /* CD Writer */
-               buffer[n+2]&0x02 ? "cd/rw " : "",    /* can read rewriteable */
-               buffer[n+4]&0x20 ? "xa/form2 " : "", /* can read xa/from2 */
-               buffer[n+5]&0x01 ? "cdda " : "",     /* can read audio data */
-               loadmech[buffer[n+6]>>5]);
-	if ((buffer[n+6] >> 5) == 0)
-		/* caddy drives can't close tray... */
-        	scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY;
+	scsi_free(buffer, 512);
+	return;
     }
+    
+    n = buffer[3]+4;
+    scsi_CDs[i].cdi.speed    = ((buffer[n+8] << 8) + buffer[n+9])/176;
+    scsi_CDs[i].readcd_known = 1;
+    scsi_CDs[i].readcd_cdda  = buffer[n+5] & 0x01;
+    /* print some capability bits */
+    printk("sr%i: scsi3-mmc drive: %dx/%dx %s%s%s%s%s\n",i,
+    	((buffer[n+14] << 8) + buffer[n+15])/176,
+	scsi_CDs[i].cdi.speed,
+	buffer[n+3]&0x01 ? "writer " : "",   /* CD Writer */
+	buffer[n+2]&0x02 ? "cd/rw " : "",    /* can read rewriteable */
+	buffer[n+4]&0x20 ? "xa/form2 " : "", /* can read xa/from2 */
+	buffer[n+5]&0x01 ? "cdda " : "",     /* can read audio data */
+	loadmech[buffer[n+6]>>5]);
+    if ((buffer[n+6] >> 5) == 0)
+	/* caddy drives can't close tray... */
+        scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY;
+    if ((buffer[n+2] & 0x8) == 0)
+	/* not a DVD drive */
+	scsi_CDs[i].cdi.mask |= CDC_DVD;
+    if ((buffer[n+3] & 0x20) == 0)
+    	/* can't write DVD-RAM media */
+	scsi_CDs[i].cdi.mask |= CDC_DVD_RAM;
+    if ((buffer[n+3] & 0x10) == 0)
+    	/* can't write DVD-R media */
+	scsi_CDs[i].cdi.mask |= CDC_DVD_R;
+    if ((buffer[n+3] & 0x2) == 0)
+    	/* can't write CD-RW media */
+	scsi_CDs[i].cdi.mask |= CDC_CD_RW;
+    if ((buffer[n+3] & 0x1) == 0)
+    	/* can't write CD-R media */
+	scsi_CDs[i].cdi.mask |= CDC_CD_R;
+
     scsi_free(buffer, 512);
+}
+
+/*
+ * sr_packet() is the entry point for the generic commands generated
+ * by the Uniform CD-ROM layer. 
+*/
+static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc)
+{
+	Scsi_Cmnd *SCpnt;
+	Scsi_Device *device = scsi_CDs[MINOR(cdi->dev)].device;
+	struct semaphore sem = MUTEX_LOCKED;
+	unsigned long flags;
+	int stat;
+	
+	/* get the device */
+	SCpnt = scsi_allocate_device(NULL, device, 1);
+	if (SCpnt == NULL)
+		return -ENODEV;	/* this just doesn't seem right /axboe */
+	
+	/* set the LUN */
+	cgc->cmd[1] |= device->lun << 5;
+
+	/* do the locking and issue the command */
+	SCpnt->request.rq_dev = cdi->dev;
+	SCpnt->request.rq_status = RQ_SCSI_BUSY;
+	/* scsi_do_cmd sets the command length */
+	SCpnt->cmd_len = 0;
+	SCpnt->request.sem = &sem;
+	spin_lock_irqsave(&io_request_lock, flags);
+	scsi_do_cmd (SCpnt, (void *)cgc->cmd, (void *)cgc->buffer, cgc->buflen,
+		     sr_init_done, SR_TIMEOUT, MAX_RETRIES);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+	down(&sem);
+	
+	stat = SCpnt->result;
+
+	/* release */
+	SCpnt->request.rq_dev = MKDEV(0,0);
+	scsi_release_command(SCpnt);
+	SCpnt = NULL;
+	
+	return stat;
 }
 
 static int sr_registered = 0;
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- /tmp/linux-2.2.12/drivers/scsi/sr_ioctl.c	Mon Aug 23 13:32:23 1999
+++ linux/drivers/scsi/sr_ioctl.c	Thu Sep  2 22:53:35 1999
@@ -18,9 +18,6 @@
 # define DEBUG
 #endif
 
-/* for now we borrow the "operation not supported" from the network folks */
-#define EDRIVE_CANT_DO_THIS  EOPNOTSUPP
-
 /* The sr_is_xa() seems to trigger firmware bugs with some drives :-(
  * It is off by default and can be turned on with this module parameter */
 static int xa_test = 0;
@@ -159,7 +156,7 @@
 {
 	u_char  sr_cmd[10];
 
-        sr_cmd[0] = TEST_UNIT_READY;
+        sr_cmd[0] = GPCMD_TEST_UNIT_READY;
         sr_cmd[1] = ((scsi_CDs[minor].device -> lun) << 5);
         sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
         return sr_do_ioctl(minor, sr_cmd, NULL, 255, 1);
@@ -169,7 +166,7 @@
 {
         u_char  sr_cmd[10];
 
-        sr_cmd[0] = START_STOP;
+        sr_cmd[0] = GPCMD_START_STOP_UNIT;
         sr_cmd[1] = ((scsi_CDs[MINOR(cdi->dev)].device -> lun) << 5);
         sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
         sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */;
@@ -239,11 +236,10 @@
 int sr_get_mcn(struct cdrom_device_info *cdi,struct cdrom_mcn *mcn)
 {
         u_char  sr_cmd[10];
-	char * buffer;
+	char buffer[32];
         int result;
-        unsigned long flags;
         	
-	sr_cmd[0] = SCMD_READ_SUBCHANNEL;
+	sr_cmd[0] = GPCMD_READ_SUBCHANNEL;
 	sr_cmd[1] = ((scsi_CDs[MINOR(cdi->dev)].device->lun) << 5);
 	sr_cmd[2] = 0x40;    /* I do want the subchannel info */
 	sr_cmd[3] = 0x02;    /* Give me medium catalog number info */
@@ -253,20 +249,11 @@
 	sr_cmd[8] = 24;
 	sr_cmd[9] = 0;
 
-        spin_lock_irqsave(&io_request_lock, flags);
-	buffer = (unsigned char*) scsi_malloc(512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if(!buffer) return -ENOMEM;
-	
 	result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24, 0);
 	
 	memcpy (mcn->medium_catalog_number, buffer + 9, 13);
         mcn->medium_catalog_number[13] = 0;
 
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(buffer, 512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	
 	return result;
 }
 
@@ -286,7 +273,7 @@
             speed *= 177;   /* Nx to kbyte/s */
         
 	memset(sr_cmd,0,12);
-	sr_cmd[0] = 0xbb; /* SET CD SPEED */
+	sr_cmd[0] = GPCMD_SET_SPEED; /* SET CD SPEED */
 	sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->lun) << 5;
 	sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */
 	sr_cmd[3] =  speed       & 0xff; /* LSB */
@@ -299,6 +286,8 @@
 /* ----------------------------------------------------------------------- */
 /* this is called by the generic cdrom driver. arg is a _kernel_ pointer,  */
 /* becauce the generic cdrom driver does the user access stuff for us.     */
+/* only cdromreadtochdr and cdromreadtocentry are left - for use with the  */
+/* sr_disk_status interface for the generic cdrom driver.                  */
 
 int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
 {
@@ -309,95 +298,12 @@
     
     switch (cmd) 
     {
-	/* Sun-compatible */
-    case CDROMPAUSE:
-	
-	sr_cmd[0] = SCMD_PAUSE_RESUME;
-	sr_cmd[1] = scsi_CDs[target].device->lun << 5;
-	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
-	sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
-	sr_cmd[8] = 0;
-	sr_cmd[9] = 0;
-	
-	result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
-        break;
-	
-    case CDROMRESUME:
-	
-	sr_cmd[0] = SCMD_PAUSE_RESUME;
-	sr_cmd[1] = scsi_CDs[target].device->lun << 5;
-	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
-	sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
-	sr_cmd[8] = 1;
-	sr_cmd[9] = 0;
-	
-	result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
-        break;
-	
-    case CDROMPLAYMSF:
-    {
-	struct cdrom_msf* msf = (struct cdrom_msf*)arg;
-
-	sr_cmd[0] = SCMD_PLAYAUDIO_MSF;
-	sr_cmd[1] = scsi_CDs[target].device->lun << 5;
-	sr_cmd[2] = 0;
-	sr_cmd[3] = msf->cdmsf_min0;
-	sr_cmd[4] = msf->cdmsf_sec0;
-	sr_cmd[5] = msf->cdmsf_frame0;
-	sr_cmd[6] = msf->cdmsf_min1;
-	sr_cmd[7] = msf->cdmsf_sec1;
-	sr_cmd[8] = msf->cdmsf_frame1;
-	sr_cmd[9] = 0;
-	
-	result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
-        break;
-    }
-
-    case CDROMPLAYBLK:
-    {
-	struct cdrom_blk* blk = (struct cdrom_blk*)arg;
-
-	sr_cmd[0] = SCMD_PLAYAUDIO10;
-	sr_cmd[1] = scsi_CDs[target].device->lun << 5;
-	sr_cmd[2] = blk->from >> 24;
-	sr_cmd[3] = blk->from >> 16;
-	sr_cmd[4] = blk->from >> 8;
-	sr_cmd[5] = blk->from;
-	sr_cmd[6] = 0;
-	sr_cmd[7] = blk->len >> 8;
-	sr_cmd[8] = blk->len;
-	sr_cmd[9] = 0;
-	
-	result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
-        break;
-    }
-		
-    case CDROMPLAYTRKIND:
-    {
-	struct cdrom_ti* ti = (struct cdrom_ti*)arg;
-
-	sr_cmd[0] = SCMD_PLAYAUDIO_TI;
-	sr_cmd[1] = scsi_CDs[target].device->lun << 5;
-	sr_cmd[2] = 0;
-	sr_cmd[3] = 0;
-	sr_cmd[4] = ti->cdti_trk0;
-	sr_cmd[5] = ti->cdti_ind0;
-	sr_cmd[6] = 0;
-	sr_cmd[7] = ti->cdti_trk1;
-	sr_cmd[8] = ti->cdti_ind1;
-	sr_cmd[9] = 0;
-	
-	result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
-        break;
-    }
-	
     case CDROMREADTOCHDR:
     {
 	struct cdrom_tochdr* tochdr = (struct cdrom_tochdr*)arg;
-	char * buffer;
-        unsigned long flags;
+	char buffer[32];
 	
-	sr_cmd[0] = SCMD_READ_TOC;
+	sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
 	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
 	sr_cmd[6] = 0;
@@ -405,29 +311,20 @@
 	sr_cmd[8] = 12;             /* LSB of length */
 	sr_cmd[9] = 0;
 	
-        spin_lock_irqsave(&io_request_lock, flags);
-	buffer = (unsigned char *) scsi_malloc(512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if(!buffer) return -ENOMEM;
-	
 	result = sr_do_ioctl(target, sr_cmd, buffer, 12, 1);
 	
 	tochdr->cdth_trk0 = buffer[2];
 	tochdr->cdth_trk1 = buffer[3];
 	
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(buffer, 512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
         break;
     }
 	
     case CDROMREADTOCENTRY:
     {
 	struct cdrom_tocentry* tocentry = (struct cdrom_tocentry*)arg;
-	unsigned char * buffer;
-        unsigned long flags;
+	unsigned char buffer[32];
 	
-	sr_cmd[0] = SCMD_READ_TOC;
+	sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
 	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) |
           (tocentry->cdte_format == CDROM_MSF ? 0x02 : 0);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
@@ -436,11 +333,6 @@
 	sr_cmd[8] = 12;            /* LSB of length */
 	sr_cmd[9] = 0;
 	
-        spin_lock_irqsave(&io_request_lock, flags);
-	buffer = (unsigned char *) scsi_malloc(512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if(!buffer) return -ENOMEM;
-	
 	result = sr_do_ioctl (target, sr_cmd, buffer, 12, 0);
 	
         tocentry->cdte_ctrl = buffer[5] & 0xf;	
@@ -454,187 +346,9 @@
 	    tocentry->cdte_addr.lba = (((((buffer[8] << 8) + buffer[9]) << 8)
                                        + buffer[10]) << 8) + buffer[11];
 	
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(buffer, 512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
         break;
     }
-	
-    case CDROMSTOP:
-	sr_cmd[0] = START_STOP;
-	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
-	sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
-	sr_cmd[4] = 0;
-	
-	result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
-        break;
-	
-    case CDROMSTART:
-	sr_cmd[0] = START_STOP;
-	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
-	sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
-	sr_cmd[4] = 1;
-	
-	result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
-        break;
-	
-    case CDROMVOLCTRL:
-    {
-	char * buffer, * mask;
-	struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg;
-        unsigned long flags;
-	
-	/* First we get the current params so we can just twiddle the volume */
-	
-	sr_cmd[0] = MODE_SENSE;
-	sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
-	sr_cmd[2] = 0xe;    /* Want mode page 0xe, CDROM audio params */
-	sr_cmd[3] = 0;
-	sr_cmd[4] = 28;
-	sr_cmd[5] = 0;
-	
-        spin_lock_irqsave(&io_request_lock, flags);
-	buffer = (unsigned char *) scsi_malloc(512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if(!buffer) return -ENOMEM;
-	
-	if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) {
-	    printk ("Hosed while obtaining audio mode page\n");
-            spin_lock_irqsave(&io_request_lock, flags);
-	    scsi_free(buffer, 512);
-            spin_unlock_irqrestore(&io_request_lock, flags);
-            break;
-	}
-	
-	sr_cmd[0] = MODE_SENSE;
-	sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
-	sr_cmd[2] = 0x4e;   /* Want the mask for mode page 0xe */
-	sr_cmd[3] = 0;
-	sr_cmd[4] = 28;
-	sr_cmd[5] = 0;
-	
-        spin_lock_irqsave(&io_request_lock, flags);
-	mask = (unsigned char *) scsi_malloc(512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if(!mask) {
-            spin_lock_irqsave(&io_request_lock, flags);
-	    scsi_free(buffer, 512);
-            spin_unlock_irqrestore(&io_request_lock, flags);
-	    result = -ENOMEM;
-            break;
-	};
-
-	if ((result = sr_do_ioctl (target, sr_cmd, mask, 28, 0))) {
-	    printk ("Hosed while obtaining mask for audio mode page\n");
-            spin_lock_irqsave(&io_request_lock, flags);
-	    scsi_free(buffer, 512);
-	    scsi_free(mask, 512);
-            spin_unlock_irqrestore(&io_request_lock, flags);
-	    break;
-	}
-	
-	/* Now mask and substitute our own volume and reuse the rest */
-	buffer[0] = 0;  /* Clear reserved field */
-	
-	buffer[21] = volctrl->channel0 & mask[21];
-	buffer[23] = volctrl->channel1 & mask[23];
-	buffer[25] = volctrl->channel2 & mask[25];
-	buffer[27] = volctrl->channel3 & mask[27];
-	
-	sr_cmd[0] = MODE_SELECT;
-	sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 0x10;    /* Params are SCSI-2 */
-	sr_cmd[2] = sr_cmd[3] = 0;
-	sr_cmd[4] = 28;
-	sr_cmd[5] = 0;
-	
-	result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0);
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(buffer, 512);
-	scsi_free(mask, 512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-        break;
-    }
-	
-    case CDROMVOLREAD:
-    {
-	char * buffer;
-	struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg;
-        unsigned long flags;
-	
-	/* Get the current params */
-	
-	sr_cmd[0] = MODE_SENSE;
-	sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
-	sr_cmd[2] = 0xe;    /* Want mode page 0xe, CDROM audio params */
-	sr_cmd[3] = 0;
-	sr_cmd[4] = 28;
-	sr_cmd[5] = 0;
-	
-        spin_lock_irqsave(&io_request_lock, flags);
-	buffer = (unsigned char *) scsi_malloc(512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if(!buffer) return -ENOMEM;
-	
-	if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) {
-	    printk ("(CDROMVOLREAD) Hosed while obtaining audio mode page\n");
-            spin_lock_irqsave(&io_request_lock, flags);
-	    scsi_free(buffer, 512);
-            spin_unlock_irqrestore(&io_request_lock, flags);
-            break;
-	}
 
-	volctrl->channel0 = buffer[21];
-	volctrl->channel1 = buffer[23];
-	volctrl->channel2 = buffer[25];
-	volctrl->channel3 = buffer[27];
-
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(buffer, 512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-        break;
-    }
-	
-    case CDROMSUBCHNL:
-    {
-	struct cdrom_subchnl* subchnl = (struct cdrom_subchnl*)arg;
-	char * buffer;
-        unsigned long flags;
-	
-	sr_cmd[0] = SCMD_READ_SUBCHANNEL;
-	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02;    /* MSF format */
-	sr_cmd[2] = 0x40;    /* I do want the subchannel info */
-	sr_cmd[3] = 0x01;    /* Give me current position info */
-	sr_cmd[4] = sr_cmd[5] = 0;
-	sr_cmd[6] = 0;
-	sr_cmd[7] = 0;
-	sr_cmd[8] = 16;
-	sr_cmd[9] = 0;
-	
-        spin_lock_irqsave(&io_request_lock, flags);
-	buffer = (unsigned char *) scsi_malloc(512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if(!buffer) return -ENOMEM;
-	
-	result = sr_do_ioctl(target, sr_cmd, buffer, 16, 0);
-	
-	subchnl->cdsc_audiostatus = buffer[1];
-	subchnl->cdsc_format = CDROM_MSF;
-	subchnl->cdsc_ctrl = buffer[5] & 0xf;
-	subchnl->cdsc_trk = buffer[6];
-	subchnl->cdsc_ind = buffer[7];
-	
-	subchnl->cdsc_reladdr.msf.minute = buffer[13];
-	subchnl->cdsc_reladdr.msf.second = buffer[14];
-	subchnl->cdsc_reladdr.msf.frame = buffer[15];
-	subchnl->cdsc_absaddr.msf.minute = buffer[9];
-	subchnl->cdsc_absaddr.msf.second = buffer[10];
-	subchnl->cdsc_absaddr.msf.frame = buffer[11];
-	
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(buffer, 512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-        break;
-    }
     default:
         return -EINVAL;
     }
@@ -672,7 +386,7 @@
 #endif
 
     memset(cmd,0,12);
-    cmd[0] = 0xbe /* READ_CD */;
+    cmd[0] = GPCMD_READ_CD; /* READ_CD */
     cmd[1] = (scsi_CDs[minor].device->lun << 5) | ((format & 7) << 2);
     cmd[2] = (unsigned char)(lba >> 24) & 0xff;
     cmd[3] = (unsigned char)(lba >> 16) & 0xff;
@@ -718,7 +432,7 @@
 #endif
     
     memset(cmd,0,12);
-    cmd[0] = READ_10;
+    cmd[0] = GPCMD_READ_10;
     cmd[1] = (scsi_CDs[minor].device->lun << 5);
     cmd[2] = (unsigned char)(lba >> 24) & 0xff;
     cmd[3] = (unsigned char)(lba >> 16) & 0xff;
@@ -773,88 +487,6 @@
     target = MINOR(cdi->dev);
     
     switch (cmd) {
-    case CDROMREADMODE1:
-    case CDROMREADMODE2:
-    case CDROMREADRAW:
-    {
-	unsigned char      *raw;
-        struct cdrom_msf   msf;
-        int                lba, rc;
-	int                blocksize = 2048;
-        unsigned long flags;
-
-        switch (cmd) {
-        case CDROMREADMODE2: blocksize = CD_FRAMESIZE_RAW0; break; /* 2336 */
-        case CDROMREADRAW:   blocksize = CD_FRAMESIZE_RAW;  break; /* 2352 */
-        }
-
-	if (copy_from_user(&msf,(void*)arg,sizeof(msf)))
-		return -EFAULT;
-        spin_lock_irqsave(&io_request_lock, flags);
-        raw = scsi_malloc(2048+512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if (!(raw))
-                return -ENOMEM;
-
-	lba = (((msf.cdmsf_min0 * CD_SECS) + msf.cdmsf_sec0)
-			* CD_FRAMES + msf.cdmsf_frame0) - CD_MSF_OFFSET;
-        if (lba < 0 || lba >= scsi_CDs[target].capacity)
-            return -EINVAL;
-
-        rc = sr_read_sector(target, lba, blocksize, raw);
-	if (!rc)
-		if (copy_to_user((void*)arg, raw, blocksize))
-			rc = -EFAULT;
-
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(raw,2048+512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	return rc;
-    }
-    case CDROMREADAUDIO:
-    {
-	unsigned char      *raw;
-        int                lba, rc=0;
-        struct cdrom_read_audio ra;
-        unsigned long flags;
-
-        if (!scsi_CDs[target].readcd_known || !scsi_CDs[target].readcd_cdda)
-            return -EINVAL;  /* -EDRIVE_DOES_NOT_SUPPORT_THIS ? */
-        
-	if (copy_from_user(&ra,(void*)arg,sizeof(ra)))
-            return -EFAULT;
-        
-        if (ra.addr_format == CDROM_LBA)
-            lba = ra.addr.lba;
-        else
-            lba = (((ra.addr.msf.minute * CD_SECS) + ra.addr.msf.second)
-                   * CD_FRAMES + ra.addr.msf.frame) - CD_MSF_OFFSET;
-
-        if (lba < 0 || lba >= scsi_CDs[target].capacity)
-            return -EINVAL;
-        spin_lock_irqsave(&io_request_lock, flags);
-        raw = scsi_malloc(2048+512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-	if (!(raw))
-            return -ENOMEM;
-
-        while (ra.nframes > 0) {
-            rc = sr_read_cd(target, raw, lba, 1, CD_FRAMESIZE_RAW);
-            if (!rc)
-		if (copy_to_user(ra.buf, raw, CD_FRAMESIZE_RAW))
-                    rc = -EFAULT;
-            if (rc)
-                break;
-
-            ra.buf     += CD_FRAMESIZE_RAW;
-            ra.nframes -= 1;
-            lba++;
-        }
-        spin_lock_irqsave(&io_request_lock, flags);
-	scsi_free(raw,2048+512);
-        spin_unlock_irqrestore(&io_request_lock, flags);
-        return rc;
-    }
     case BLKRAGET:
 	if (!arg)
 		return -EINVAL;
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/include/linux/cdrom.h linux/include/linux/cdrom.h
--- /tmp/linux-2.2.12/include/linux/cdrom.h	Mon Aug 23 13:32:23 1999
+++ linux/include/linux/cdrom.h	Thu Sep  2 22:49:08 1999
@@ -11,6 +11,8 @@
 #ifndef	_LINUX_CDROM_H
 #define	_LINUX_CDROM_H
 
+#include <linux/types.h>
+
 /*******************************************************
  * As of Linux 2.1.x, all Linux CD-ROM application programs will use this 
  * (and only this) include file.  It is my hope to provide Linux with
@@ -94,16 +96,26 @@
 #define CDROMPLAYBLK		0x5317	/* (struct cdrom_blk) */
 
 /* 
- * These ioctls are used only used in optcd.c
+ * These ioctls are only used in optcd.c
  */
 #define CDROMREADALL		0x5318	/* read all 2646 bytes */
-#define CDROMCLOSETRAY		0x5319	/* pendant of CDROMEJECT */
+
+/* 
+ * These ioctls are (now) only in ide-cd.c for controlling 
+ * drive spindown time.  They should be implemented in the
+ * Uniform driver, via generic packet commands, GPCMD_MODE_SELECT_10,
+ * GPCMD_MODE_SENSE_10 and the GPMODE_POWER_PAGE...
+ *  -Erik
+ */
+#define CDROMGETSPINDOWN        0x531d
+#define CDROMSETSPINDOWN        0x531e
 
 /* 
  * These ioctls are implemented through the uniform CD-ROM driver
  * They _will_ be adopted by all CD-ROM drivers, when all the CD-ROM
  * drivers are eventually ported to the uniform CD-ROM driver interface.
  */
+#define CDROMCLOSETRAY		0x5319	/* pendant of CDROMEJECT */
 #define CDROM_SET_OPTIONS	0x5320  /* Set behavior options */
 #define CDROM_CLEAR_OPTIONS	0x5321  /* Clear behavior options */
 #define CDROM_SELECT_SPEED	0x5322  /* Set the CD-ROM speed */
@@ -119,6 +131,15 @@
 /* This ioctl is only used by sbpcd at the moment */
 #define CDROMAUDIOBUFSIZ        0x5382	/* set the audio buffer size */
 
+/* DVD-ROM Specific ioctls */
+#define DVD_READ_STRUCT		0x5390  /* Read structure */
+#define DVD_WRITE_STRUCT	0x5391  /* Write structure */
+#define DVD_AUTH		0x5392  /* Authentication */
+
+#define CDROM_SEND_PACKET	0x5393	/* send a packet to the drive */
+#define CDROM_NEXT_WRITABLE	0x5394	/* get next writable block */
+#define CDROM_LAST_WRITTEN	0x5395	/* get last block written on disc */
+
 /*******************************************************
  * CDROM IOCTL structures
  *******************************************************/
@@ -243,6 +264,17 @@
 	unsigned short len;
 };
 
+#define CDROM_PACKET_SIZE	12
+
+/* for CDROM_PACKET_COMMAND ioctl */
+struct cdrom_generic_command
+{
+	unsigned char 	cmd[CDROM_PACKET_SIZE];
+	unsigned char 	*buffer;
+	unsigned int 	buflen;
+	int		stat;
+};
+
 
 /*
  * A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336, 
@@ -321,14 +353,6 @@
 #define	CDROM_AUDIO_ERROR	0x14	/* audio play stopped due to error */
 #define	CDROM_AUDIO_NO_STATUS	0x15	/* no current audio status to return */
 
-/* CD-ROM-specific SCSI command opcodes */
-#define SCMD_READ_TOC		0x43	/* read table of contents */
-#define SCMD_PLAYAUDIO_MSF	0x47	/* play data at time offset */
-#define SCMD_PLAYAUDIO_TI	0x48	/* play data at track/index */
-#define SCMD_PAUSE_RESUME	0x4B	/* pause/resume audio */
-#define SCMD_READ_SUBCHANNEL	0x42	/* read SC info on playing disc */
-#define SCMD_PLAYAUDIO10	0x45	/* play data at logical block */
-
 /* capability flags used with the uniform CD-ROM driver */ 
 #define CDC_CLOSE_TRAY		0x1     /* caddy systems _can't_ close */
 #define CDC_OPEN_TRAY		0x2     /* but _can_ eject.  */
@@ -342,6 +366,12 @@
 #define CDC_RESET               0x200   /* hard reset device */
 #define CDC_IOCTLS              0x400   /* driver has non-standard ioctls */
 #define CDC_DRIVE_STATUS        0x800   /* driver implements drive status */
+#define CDC_GENERIC_PACKET	0x1000	/* driver implements generic packets */
+#define CDC_CD_R		0x2000	/* drive is a CD-R */
+#define CDC_CD_RW		0x4000	/* drive is a CD-RW */
+#define CDC_DVD			0x8000	/* drive is a DVD */
+#define CDC_DVD_R		0x10000	/* drive can write DVD-R */
+#define CDC_DVD_RAM		0x20000	/* drive can write DVD-RAM */
 
 /* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
 #define CDS_NO_INFO		0	/* if not implemented */
@@ -370,6 +400,252 @@
 #define CDSL_NONE       	((int) (~0U>>1)-1)
 #define CDSL_CURRENT    	((int) (~0U>>1))
 
+/*********************************************************************
+ * Generic Packet commands, MMC commands, and such
+ *********************************************************************/
+
+ /* The generic packet command opcodes for CD/DVD Logical Units,
+ * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+#define GPCMD_BLANK			    0xa1
+#define GPCMD_CLOSE_TRACK		    0x5b
+#define GPCMD_FLUSH_CACHE		    0x35
+#define GPCMD_FORMAT_UNIT		    0x04
+#define GPCMD_GET_CONFIGURATION		    0x46
+#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
+#define GPCMD_GET_PERFORMANCE		    0xac
+#define GPCMD_INQUIRY			    0x12
+#define GPCMD_LOAD_UNLOAD		    0xa6
+#define GPCMD_MECHANISM_STATUS		    0xbd
+#define GPCMD_MODE_SELECT_10		    0x55
+#define GPCMD_MODE_SENSE_10		    0x5a
+#define GPCMD_PAUSE_RESUME		    0x4b
+#define GPCMD_PLAY_AUDIO_10		    0x45
+#define GPCMD_PLAY_AUDIO_MSF		    0x47
+#define GPCMD_PLAY_CD			    0xbc
+#define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL  0x1e
+#define GPCMD_READ_10			    0x28
+#define GPCMD_READ_12			    0xa8
+#define GPCMD_READ_CDVD_CAPACITY	    0x25
+#define GPCMD_READ_CD			    0xbe
+#define GPCMD_READ_CD_MSF		    0xb9
+#define GPCMD_READ_DISC_INFO		    0x51
+#define GPCMD_READ_DVD_STRUCTURE	    0xad
+#define GPCMD_READ_FORMAT_CAPACITIES	    0x23
+#define GPCMD_READ_HEADER		    0x44
+#define GPCMD_READ_TRACK_RZONE_INFO	    0x52
+#define GPCMD_READ_SUBCHANNEL		    0x42
+#define GPCMD_READ_TOC_PMA_ATIP		    0x43
+#define GPCMD_REPAIR_RZONE_TRACK	    0x58
+#define GPCMD_REPORT_KEY		    0xa4
+#define GPCMD_REQUEST_SENSE		    0x03
+#define GPCMD_RESERVE_RZONE_TRACK	    0x53
+#define GPCMD_SCAN			    0xba
+#define GPCMD_SEEK			    0x2b
+#define GPCMD_SEND_DVD_STRUCTURE	    0xad
+#define GPCMD_SEND_EVENT		    0xa2
+#define GPCMD_SEND_KEY			    0xa3
+#define GPCMD_SEND_OPC			    0x54
+#define GPCMD_SET_READ_AHEAD		    0xa7
+#define GPCMD_SET_STREAMING		    0xb6
+#define GPCMD_START_STOP_UNIT		    0x1b
+#define GPCMD_STOP_PLAY_SCAN		    0x4e
+#define GPCMD_TEST_UNIT_READY		    0x00
+#define GPCMD_VERIFY_10			    0x2f
+#define GPCMD_WRITE_10			    0x2a
+#define GPCMD_WRITE_AND_VERIFY_10	    0x2e
+/* This is listed as optional in ATAPI 2.6, but is (curiously) 
+ * missing from Mt. Fuji, Table 57.  It _is_ mentioned in Mt. Fuji
+ * Table 377 as an MMC command for SCSi devices though...  Most ATAPI
+ * drives support it. */
+#define GPCMD_SET_SPEED			    0xbb
+/* This seems to be a SCSI specific CD-ROM opcode 
+ * to play data at track/index */
+#define GPCMD_PLAYAUDIO_TI		    0x48
+
+/* Is this really used by anything?  I couldn't find these...*/
+#if 0
+/* MMC2/MTFuji Opcodes */
+#define ERASE			0x2c
+#define READ_BUFFER		0x3c
+#endif
+
+
+
+
+/* Mode page codes for mode sense/set */
+#define GPMODE_R_W_ERROR_PAGE	    0x1
+#define GPMODE_WRITE_PARMS_PAGE	    0x5
+#define GPMODE_AUDIO_CTL_PAGE	    0xe
+#define GPMODE_POWER_PAGE	    0x1a
+#define GPMODE_FAULT_FAIL_PAGE	    0x1c
+#define GPMODE_TO_PROTECT_PAGE	    0x1d
+#define GPMODE_CAPABILITIES_PAGE    0x2a
+#define GPMODE_ALL_PAGES	    0x3f
+/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
+ * of MODE_SENSE_POWER_PAGE */
+#define GPMODE_CDROM_PAGE              0x0d
+
+
+
+/* DVD struct types */
+#define DVD_STRUCT_PHYSICAL	0x00
+#define DVD_STRUCT_COPYRIGHT	0x01
+#define DVD_STRUCT_DISCKEY	0x02
+#define DVD_STRUCT_BCA		0x03
+#define DVD_STRUCT_MANUFACT	0x04
+
+struct dvd_layer {
+	u_char book_version	: 4;
+	u_char book_type	: 4;
+	u_char min_rate		: 4;
+	u_char disc_size	: 4;
+	u_char layer_type	: 4;
+	u_char track_path	: 1;
+	u_char nlayers		: 2;
+	u_char track_density	: 4;
+	u_char linear_density	: 4;
+	u_char bca		: 1;
+	u_char start_sector;
+	u_char end_sector;
+	u_char end_sector_l0;
+};
+
+struct dvd_physical {
+	u_char type;
+	u_char layer_num;
+	struct dvd_layer layer[4];
+};
+
+struct dvd_copyright {
+	u_char type;
+
+	u_char layer_num;
+	u_char cpst;
+	u_char rmi;
+};
+
+struct dvd_disckey {
+	u_char type;
+
+	unsigned agid			: 2;
+	u_char value[2048];
+};
+
+struct dvd_bca {
+	u_char type;
+
+	int len;
+	u_char value[188];
+};
+
+struct dvd_manufact {
+	u_char type;
+
+	u_char layer_num;
+	int len;
+	u_char value[2048];
+};
+
+typedef union {
+	u_char type;
+
+	struct dvd_physical	physical;
+	struct dvd_copyright	copyright;
+	struct dvd_disckey	disckey;
+	struct dvd_bca		bca;
+	struct dvd_manufact	manufact;
+} dvd_struct;
+
+/*
+ * DVD authentication ioctl
+ */
+
+/* Authentication states */
+#define DVD_LU_SEND_AGID	0
+#define DVD_HOST_SEND_CHALLENGE	1
+#define DVD_LU_SEND_KEY1	2
+#define DVD_LU_SEND_CHALLENGE	3
+#define DVD_HOST_SEND_KEY2	4
+
+/* Termination states */
+#define DVD_AUTH_ESTABLISHED	5
+#define DVD_AUTH_FAILURE	6
+
+/* Other functions */
+#define DVD_LU_SEND_TITLE_KEY	7
+#define DVD_LU_SEND_ASF		8
+#define DVD_INVALIDATE_AGID	9
+
+/* State data */
+typedef u_char dvd_key[5];		/* 40-bit value, MSB is first elem. */
+typedef u_char dvd_challenge[10];	/* 80-bit value, MSB is first elem. */
+
+struct dvd_lu_send_agid {
+	u_char type;
+	unsigned agid		: 2;
+};
+
+struct dvd_host_send_challenge {
+	u_char type;
+	unsigned agid		: 2;
+
+	dvd_challenge chal;
+};
+
+struct dvd_send_key {
+	u_char type;
+	unsigned agid		: 2;
+
+	dvd_key key;
+};
+
+struct dvd_lu_send_challenge {
+	u_char type;
+	unsigned agid		: 2;
+
+	dvd_challenge chal;
+};
+
+#define DVD_CPM_NO_COPYRIGHT	0
+#define DVD_CPM_COPYRIGHTED	1
+
+#define DVD_CP_SEC_NONE		0
+#define DVD_CP_SEC_EXIST	1
+
+#define DVD_CGMS_UNRESTRICTED	0
+#define DVD_CGMS_SINGLE		2
+#define DVD_CGMS_RESTRICTED	3
+
+struct dvd_lu_send_title_key {
+	u_char type;
+	unsigned agid		: 2;
+
+	dvd_key title_key;
+	int lba;
+	unsigned cpm		: 1;
+	unsigned cp_sec		: 1;
+	unsigned cgms		: 2;
+};
+
+struct dvd_lu_send_asf {
+	u_char type;
+	unsigned agid		: 2;
+
+	unsigned asf		: 1;
+};
+
+typedef union {
+	u_char type;
+
+	struct dvd_lu_send_agid		lsa;
+	struct dvd_host_send_challenge	hsc;
+	struct dvd_send_key		lsk;
+	struct dvd_lu_send_challenge	lsc;
+	struct dvd_send_key		hsk;
+	struct dvd_lu_send_title_key	lstk;
+	struct dvd_lu_send_asf		lsasf;
+} dvd_authinfo;
+
 #ifdef __KERNEL__
 /* Uniform cdrom data structures for cdrom.c */
 struct cdrom_device_info {
@@ -413,6 +689,9 @@
 /* driver specifications */
 	const int capability;   /* capability flags */
 	int n_minors;           /* number of active minor devices */
+	/* handle uniform packets for scsi type devices (scsi,atapi) */
+	int (*generic_packet) (struct cdrom_device_info *,
+			       struct cdrom_generic_command *);
 };
 
 /* the general file operations structure: */
@@ -428,6 +707,95 @@
     long error;
 } tracktype;
 extern void cdrom_count_tracks(struct cdrom_device_info *cdi,tracktype* tracks);
+extern int cdrom_get_next_writable(kdev_t dev, long *next_writable);
+extern int cdrom_get_last_written(kdev_t dev, long *last_written);
+
+typedef struct {
+	__u16 disc_information_length;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved1			: 3;
+        __u8 erasable			: 1;
+        __u8 border_status		: 2;
+        __u8 disc_border		: 2;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+        __u8 disc_border		: 2;
+        __u8 border_status		: 2;
+        __u8 erasable			: 1;
+	__u8 reserved1			: 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	__u8 n_first_track;
+	__u8 n_sessions_lsb;
+	__u8 first_track_lsb;
+	__u8 last_track_lsb;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 did_v			: 1;
+        __u8 dbc_v			: 1;
+        __u8 uru			: 1;
+        __u8 reserved2			: 5;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+        __u8 reserved2			: 5;
+        __u8 uru			: 1;
+        __u8 dbc_v			: 1;
+	__u8 did_v			: 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	__u8 disc_type;
+	__u8 n_sessions_msb;
+	__u8 first_track_msb;
+	__u8 last_track_msb;
+	__u32 disc_id;
+	__u32 lead_in;
+	__u32 lead_out;
+	__u8 disc_bar_code[8];
+	__u8 reserved3;
+	__u8 n_opc;
+} disc_information;
+
+typedef struct {
+	__u16 track_information_length;
+	__u8 track_lsb;
+	__u8 session_lsb;
+	__u8 reserved1;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved2			: 2;
+        __u8 damage			: 1;
+        __u8 copy			: 1;
+        __u8 track_mode			: 4;
+	__u8 rt				: 1;
+	__u8 blank			: 1;
+	__u8 packet			: 1;
+	__u8 fp				: 1;
+	__u8 data_mode			: 4;
+	__u8 reserved3			: 6;
+	__u8 lra_v			: 1;
+	__u8 nwa_v			: 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+        __u8 track_mode			: 4;
+        __u8 copy			: 1;
+        __u8 damage			: 1;
+	__u8 reserved2			: 2;
+	__u8 data_mode			: 4;
+	__u8 fp				: 1;
+	__u8 packet			: 1;
+	__u8 blank			: 1;
+	__u8 rt				: 1;
+	__u8 nwa_v			: 1;
+	__u8 lra_v			: 1;
+	__u8 reserved3			: 6;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	__u32 track_start;
+	__u32 next_writable;
+	__u32 free_blocks;
+	__u32 fixed_packet_size;
+	__u32 track_size;
+	__u32 last_rec_address;
+} track_information;
+
 #endif  /* End of kernel only stuff */ 
 
 #endif  /* _LINUX_CDROM_H */
diff -ur --exclude-from /home/axboe/cdrom/exclude-from /tmp/linux-2.2.12/include/linux/sysctl.h linux/include/linux/sysctl.h
--- /tmp/linux-2.2.12/include/linux/sysctl.h	Mon Aug 23 13:32:23 1999
+++ linux/include/linux/sysctl.h	Tue Aug 10 17:17:44 1999
@@ -430,7 +430,12 @@
 
 /* /proc/sys/dev/cdrom */
 enum {
-	DEV_CDROM_INFO=1
+	DEV_CDROM_INFO=1,
+	DEV_CDROM_AUTOCLOSE=2,
+	DEV_CDROM_AUTOEJECT=3,
+	DEV_CDROM_DEBUG=4,
+	DEV_CDROM_LOCK=5,
+	DEV_CDROM_CHECK_MEDIA=6
 };
 
 #ifdef __KERNEL__
