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	Thu Sep  9 12:27:35 1999
+++ linux/drivers/block/ide-cd.c	Tue Sep 14 21:44:13 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 4) 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://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r01.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...
  *
@@ -27,14 +29,8 @@
  *   This will allow us to get automagically notified when the media changes
  *   on ATAPI drives (something the stock ATAPI spec is lacking).  Looks
  *   very cool.  I discovered its existance the other day at work...
- * -Query the drive to find what features are available before trying to
- *   use them (like trying to close the tray in drives that can't).
  * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
  *   boot
- * -Integrate DVD-ROM support in driver. Thanks to Merete Gotsæd-Petersen
- *   of Pioneer Denmark for providing me with a drive for testing.
- * -Implement Features and Profiles.
- *
  *
  * ----------------------------------
  * 1.00  Oct 31, 1994 -- Initial version.
@@ -243,12 +239,49 @@
  *                         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.
+ *
+ * 4.56  Sep 12, 1999	- Removed changer support - it is now in the
+ *			  Uniform layer.
+ *			- Added partition based multisession handling.
+ *			- Mode sense and mode select moved to the
+ *			  Uniform layer.
+ *			- Fixed a problem with WPI CDS-32X drive - it
+ *			  failed the capabilities.
+ *
  *
  *************************************************************************/
+ 
+#define IDECD_VERSION "4.56"
 
-#define IDECD_VERSION "4.53"
-
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -258,6 +291,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 +336,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 +378,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 +508,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 +550,9 @@
 				int *stat_ret)
 {
 	struct request *rq = HWGROUP(drive)->rq;
-	int stat, err, sense_key, cmd;
-
+	int stat, cmd, err, sense_key;
+	struct packet_command *pc = (struct packet_command *) rq->buffer;
+	
 	/* Check for errors. */
 	stat = GET_STAT();
 	*stat_ret = stat;
@@ -524,8 +560,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)
@@ -539,8 +575,6 @@
 			   from the drive (probably while trying
 			   to recover from a former error).  Just give up. */
 
-			struct packet_command *pc = (struct packet_command *)
-				                      rq->buffer;
 			pc->stat = 1;
 			cdrom_end_request (1, drive);
 			ide_error (drive, "request sense failure", stat);
@@ -549,23 +583,11 @@
 		} else if (cmd == PACKET_COMMAND) {
 			/* All other functions, except for READ. */
 
-			struct packet_command *pc = (struct packet_command *)
-				                      rq->buffer;
 			struct semaphore *sem = NULL;
 
 			/* Check for tray open. */
 			if (sense_key == NOT_READY) {
 				cdrom_saw_media_change (drive);
-#if 0	/* let the upper layers do the complaining */
-				/* Print an error message to the syslog.
-				   Exception: don't print anything if this
-				   is a read subchannel command.  This is
-				   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)
-					printk ("%s: tray open or drive not ready\n", drive->name);
-#endif
 			} else if (sense_key == UNIT_ATTENTION) {
 				/* Check for media change. */
 				cdrom_saw_media_change (drive);
@@ -670,7 +692,8 @@
 
 	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
 	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
-	OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+	if (IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
  
 	if (info->dma)
 		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
@@ -1058,7 +1081,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 +1126,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 +1190,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 +1210,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 +1237,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 +1254,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 +1305,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);
@@ -1334,7 +1334,6 @@
 	if (pc->sense_data == NULL)
 		pc->sense_data = &my_reqbuf;
 	pc->sense_data->sense_key = 0;
-
 	/* Start of retry loop. */
 	do {
 		ide_init_drive_cmd (&req);
@@ -1359,10 +1358,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;
 		}
 
@@ -1376,20 +1375,22 @@
 	else {
 		/* The command succeeded.  If it was anything other than
 		   a request sense, eject, or door lock command,
-		   and we think that the door is presently, lock it again.
-		   (The door was probably unlocked via an explicit
+		   and we think that the door is presently unlocked, 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.
  */
@@ -1490,17 +1491,19 @@
 		    struct atapi_request_sense *reqbuf)
 {
 	struct packet_command pc;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
 
 	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 
            switch CDs instead of supporting the LOAD_UNLOAD opcode   */
 
-        pc.c[7] = CDROM_STATE_FLAGS (drive)->sanyo_slot % 3;
+        pc.c[7] = cdi->sanyo_slot % 3;
 #endif /* not STANDARD_ATAPI */
 
 	return cdrom_queue_packet_command (drive, &pc);
@@ -1515,7 +1518,7 @@
 	struct atapi_request_sense my_reqbuf;
 	int stat;
 	struct packet_command pc;
-
+	
 	if (reqbuf == NULL)
 		reqbuf = &my_reqbuf;
 
@@ -1526,7 +1529,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 +1564,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 +1584,8 @@
 		     struct atapi_request_sense *reqbuf)
 {
 	struct {
-		unsigned lba;
-		unsigned blocklen;
+		__u32 lba;
+		__u32 blocklen;
 	} capbuf;
 
 	int stat;
@@ -1618,7 +1594,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 +1618,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);
@@ -1654,12 +1630,12 @@
 
 /* Try to read the entire TOC for the disk into our internal buffer. */
 static int
-cdrom_read_toc (ide_drive_t *drive,
-		struct atapi_request_sense *reqbuf)
+cdrom_read_toc (ide_drive_t *drive, struct atapi_request_sense *reqbuf)
 {
 	int stat, ntracks, i;
 	struct cdrom_info *info = drive->driver_data;
 	struct atapi_toc *toc = info->toc;
+	int minor = drive->select.b.unit << PARTN_BITS;
 	struct {
 		struct atapi_toc_header hdr;
 		struct atapi_toc_entry  ent;
@@ -1670,11 +1646,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 +1661,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 +1676,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, 0, (char *)&toc->hdr,
 				    sizeof (struct atapi_toc_header) +
-				    (ntracks+1) *
-				      sizeof (struct atapi_toc_entry),
-				    reqbuf);
+				    (ntracks + 1) *
+				    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;
+	}
 	if (stat) return stat;
+
 	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
 
 #if ! STANDARD_ATAPI
@@ -1732,10 +1739,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, 0, 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,16 +1764,39 @@
 	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, minor,
+				     (long *)&toc->capacity);
+	if (stat)
+#endif
 	stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
 	if (stat) toc->capacity = 0x1fffff;
 
-	HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
-		= (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
-	drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+	/* for general /dev/cdrom like mounting, one big disc */
+	HWIF(drive)->gd->sizes[minor] = (toc->capacity * SECTORS_PER_FRAME) >>
+					(BLOCK_SIZE_BITS - 9);
 
 	/* Remember that we've read this stuff. */
 	CDROM_STATE_FLAGS (drive)->toc_valid = 1;
 
+	/* should be "if multisession", but it does no harm. */
+	if (ntracks == 1)
+		return 0;
+
+	/* setup each minor to respond to a session */
+	minor++;
+	i = toc->hdr.first_track;
+	while ((i <= ntracks) && ((minor & CD_PART_MASK) < CD_PART_MAX)) {
+		drive->part[minor & PARTN_MASK].start_sect = 0;
+ 		drive->part[minor & PARTN_MASK].nr_sects = (toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) << (BLOCK_SIZE_BITS - 9);
+		HWIF(drive)->gd->sizes[minor] = (toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
+		blksize_size[HWIF(drive)->major][minor] = CD_FRAMESIZE;
+		i++;
+		minor++;
+	}
+
 	return 0;
 }
 
@@ -1775,7 +1813,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;
@@ -1784,46 +1822,6 @@
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
-
-/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */
-static int
-cdrom_mode_sense (ide_drive_t *drive, int pageno, int modeflag,
-                  char *buf, int buflen,
-		  struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.buffer =  buf;
-	pc.buflen = buflen;
-	pc.c[0] = MODE_SENSE_10;
-	pc.c[2] = pageno | (modeflag << 6);
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-static int
-cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen,
-		   struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.buffer =  buf;
-	pc.buflen = - buflen;
-	pc.c[0] = MODE_SELECT_10;
-	pc.c[1] = 0x10;
-	pc.c[2] = pageno;
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
 /* ATAPI cdrom drives are free to select the speed you request or any slower
    rate :-( Requesting too fast a speed will _not_ produce an error. */
 static int
@@ -1839,7 +1837,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 +1853,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 +1860,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,86 +1878,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,
-		   struct atapi_request_sense *reqbuf)
-{
-#if ! STANDARD_ATAPI
-	/* if the drive is a Sanyo 3 CD changer then TEST_UNIT_READY
-           (used in the cdrom_check_status function) is used to 
-           switch CDs instead of LOAD_UNLOAD */
-
-	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
-
-        	if ((slot == 1) || (slot == 2))
-			CDROM_STATE_FLAGS (drive)->sanyo_slot = slot;
-		else if (slot >= 0)
-			CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
-		else
-			return 0;
-
-		return cdrom_check_status (drive, reqbuf);
-
-	}
-	else
-#endif /*not STANDARD_ATAPI */
-	{
-
-		/* ATAPI Rev. 2.2+ standard for requesting switching of
-                   CDs in a multiplatter device */
-
-		struct packet_command pc;
-
-		memset (&pc, 0, sizeof (pc));
-		pc.sense_data = reqbuf;
-
-		pc.c[0] = LOAD_UNLOAD;
-		pc.c[4] = 2 + (slot >= 0);
-		pc.c[8] = slot;
-		return cdrom_queue_packet_command (drive, &pc);
-
-	}
-}
-
 
 /* This gets the mechanism status per ATAPI draft spec 2.6 */
 static int
@@ -2039,13 +1891,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,234 +1938,70 @@
 		 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,
 			 unsigned int cmd, unsigned long arg)
-			 
 {
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_generic_command cgc;
+	char buffer[16];
+	int stat;
 
+	init_cdrom_command(&cgc, buffer, sizeof(buffer));
 
+	/* These will be moved into the Uniform layer shortly... */
 	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,
- 					 sizeof (buffer), NULL);
- 		if (stat) return stat;
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
 
  		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
 
- 		return cdrom_mode_select (drive, PAGE_CDROM, buffer,
- 					  sizeof (buffer), NULL);			
+ 		return cdrom_mode_select(cdi, &cgc);
  	} 
  
  	case CDROMGETSPINDOWN: {
  		char spindown;
- 		char buffer[16];
- 		int stat;
  
- 		stat = verify_area (VERIFY_WRITE, (void *) arg,
-                                    sizeof (char));
- 		if (stat) return stat;
- 
- 		stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
-                                         sizeof (buffer), NULL);
- 		if (stat) return stat;
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			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 +2011,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 +2038,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 +2080,6 @@
 	return cdrom_eject (drive, !position, NULL);
 }
 
-
 static
 int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
 {
@@ -2543,22 +2091,23 @@
 int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
 {
         int stat, attempts = 3;
-        struct {
-                char pad[8];
-                struct atapi_capabilities_page cap;
-        } buf;
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	struct atapi_request_sense reqbuf;
+	struct cdrom_generic_command cgc;
+	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
 	stat=cdrom_select_speed (drive, speed, &reqbuf);
 	if (stat<0)
 		return stat;
 
+	init_cdrom_command(&cgc, &buf, sizeof(buf));
 	/* Now with that done, update the speed fields */
         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,
-                                        (char *)&buf, sizeof (buf), NULL);
+                stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
         } while (stat);
 
         /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
@@ -2577,93 +2126,6 @@
         return 0;
 }
 
-
-static
-int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_info *info = drive->driver_data;
-
-	struct atapi_request_sense my_reqbuf;
-	int stat;
-	int nslots, curslot;
-
-	if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) 
-		return -EDRIVE_CANT_DO_THIS;
-
-#if ! STANDARD_ATAPI
-	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
-		nslots = 3;
-		curslot = CDROM_STATE_FLAGS (drive)->sanyo_slot;
-		if (curslot == 3)
-			curslot = 0;
-	} else
-#endif /* not STANDARD_ATAPI */
-	{
-		stat = cdrom_read_changer_info (drive);
-		if (stat)
-			return stat;
-
-		nslots = info->changer_info->hdr.nslots;
-		curslot = info->changer_info->hdr.curslot;
-	}
-
-	if (slot == curslot)
-		return curslot;
-
-	if (slot == CDSL_CURRENT)
-		return curslot;
-
-	if (slot != CDSL_NONE && (slot < 0 || slot >= nslots))
-		return -EINVAL;
-
-	if (drive->usage > 1)
-		return -EBUSY;
-
-	if (slot == CDSL_NONE) {
-		(void) cdrom_load_unload (drive, -1, NULL);
-		cdrom_saw_media_change (drive);
-		(void) cdrom_lockdoor (drive, 0, NULL);
-		return 0;
-	}
-	else {
-		int was_locked;
-
-		if (
-#if ! STANDARD_ATAPI
-		    CDROM_STATE_FLAGS (drive)->sanyo_slot == 0 &&
-#endif
-		    info->changer_info->slots[slot].disc_present == 0) {
-			return -ENOMEDIUM;
-		}
-
-		was_locked = CDROM_STATE_FLAGS (drive)->door_locked;
-		if (was_locked)
-			(void) cdrom_lockdoor (drive, 0, NULL);
-
-		stat = cdrom_load_unload (drive, slot, NULL);
-		cdrom_saw_media_change (drive);
-		if (stat)
-			return stat;
-			
-		stat = cdrom_check_status (drive, &my_reqbuf);
-		if (stat && my_reqbuf.sense_key == NOT_READY)
-			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);
-
-		return slot;
-	}
-}
-
-
 static
 int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
 {
@@ -2688,7 +2150,7 @@
 	}
 
 #if ! STANDARD_ATAPI
-	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0)
+	else if (cdi->sanyo_slot > 0)
 		return CDS_NO_INFO;
 #endif /* not STANDARD_ATAPI */
 
@@ -2710,15 +2172,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 +2183,6 @@
 	return 0;
 }
 
-
 static
 int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
 		       struct cdrom_mcn *mcn_info)
@@ -2760,9 +2216,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;
@@ -2770,7 +2226,7 @@
 	}
 
 #if ! STANDARD_ATAPI
-	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+	else if (cdi->sanyo_slot > 0) {
 		retval = 0;
 	}
 #endif /* not STANDARD_ATAPI */
@@ -2788,7 +2244,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;
 }
 
@@ -2824,7 +2290,7 @@
 	ide_cdrom_tray_move,    /* tray_move */
 	ide_cdrom_lock_door,    /* lock_door */
 	ide_cdrom_select_speed, /* select_speed */
-	ide_cdrom_select_disc, /* select_disc */
+	NULL,			/* select_disc */
 	ide_cdrom_get_last_session, /* get_last_session */
 	ide_cdrom_get_mcn, /* get_mcn */
 	ide_cdrom_reset, /* reset */
@@ -2832,9 +2298,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)
@@ -2843,13 +2311,32 @@
 	struct cdrom_device_info *devinfo = &info->devinfo;
 	int minor = (drive->select.b.unit)<<PARTN_BITS;
 
-	devinfo->dev = MKDEV (HWIF(drive)->major, minor);
+	devinfo->dev = MKDEV (HWIF(drive)->major, minor | CD_PART_MASK);
 	devinfo->ops = &ide_cdrom_dops;
 	devinfo->mask = 0;
 	*(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
 	*(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,8 +2344,11 @@
 static
 int ide_cdrom_probe_capabilities (ide_drive_t *drive)
 {
-	int stat, nslots = 0, attempts = 3;
- 	struct {
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	int stat, nslots = 1, attempts = 3;
+	struct cdrom_generic_command cgc;
+	struct {
 		char pad[8];
 		struct atapi_capabilities_page cap;
 	} buf;
@@ -2866,11 +2356,19 @@
 	if (CDROM_CONFIG_FLAGS (drive)->nec260)
 		return nslots;
 
+	init_cdrom_command(&cgc, &buf, sizeof(buf));
+	/* we have to cheat a little here. the packet will eventually
+	 * be queued with ide_cdrom_packet(), which extracts the
+	 * drive from cdi->handle. Since this device hasn't been
+	 * registered with the Uniform layer yet, it can't do this.
+	 * Same goes cdi->ops.
+	 */
+	cdi->handle = (ide_drive_t *) drive;
+	cdi->ops = &ide_cdrom_dops;
 	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,
-				 	(char *)&buf, sizeof (buf), NULL);
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
 	} while (stat);
 
 	if (buf.cap.lock == 0)
@@ -2888,10 +2386,14 @@
 	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) {
+	if (cdi->sanyo_slot > 0) {
 		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
 		nslots = 3;
 	}
@@ -2928,10 +2430,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", 
@@ -2943,7 +2445,19 @@
         else 	
         	printk (" drive");
 
-	printk (", %dkB Cache\n", ntohs(buf.cap.buffer_size));
+	printk (", %dkB Cache", ntohs(buf.cap.buffer_size));
+
+	if (drive->using_dma) {
+		if ((drive->id->field_valid & 4) &&
+		    (drive->id->dma_ultra & (drive->id->dma_ultra >> 8) & 7)) {
+			printk(", UDMA");	/* UDMA BIOS-enabled! */
+		} else if (drive->id->field_valid & 4) {
+			printk(", (U)DMA");	/* Can be BIOS-enabled! */
+		} else {
+			printk(", DMA");
+		}
+	}
+	printk("\n");
 
 	return nslots;
 }
@@ -2959,23 +2473,24 @@
 	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
 }
 
+
 static
 int ide_cdrom_setup (ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	int minor = drive->select.b.unit << PARTN_BITS;
 	int nslots;
 
-	kdev_t dev = MKDEV (HWIF (drive)->major,
-			    drive->select.b.unit << PARTN_BITS);
+	kdev_t dev = MKDEV(HWIF(drive)->major, minor);
 
 	set_device_ro (dev, 1);
-	blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] =
-		CD_FRAMESIZE;
+	blksize_size[HWIF(drive)->major][minor] = CD_FRAMESIZE;
 
 	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 +2512,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;
@@ -3013,7 +2530,7 @@
 #if ! STANDARD_ATAPI
 	/* by default Sanyo 3 CD changer support is turned off and
            ATAPI Rev 2.2+ standard support for CD changers is used */
-	CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
+	cdi->sanyo_slot = 0;
 
 	CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
 	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
@@ -3065,7 +2582,7 @@
                          (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) ||
                          (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) {
                         /* uses CD in slot 0 when value is set to 3 */
-                        CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
+                        cdi->sanyo_slot = 3;
                 }
 
 
@@ -3128,7 +2645,6 @@
 			(drive->select.b.unit)<<PARTN_BITS));
 }
 
-
 static
 int ide_cdrom_cleanup(ide_drive_t *drive)
 {
@@ -3213,11 +2729,12 @@
 	MOD_INC_USE_COUNT;
 	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
 		/* skip drives that we were told to ignore */
-		if (ignore != NULL)
+		if (ignore != NULL) {
 			if (strstr(ignore, drive->name)) {
 				printk("ide-cd: ignoring drive %s\n", drive->name);
 				continue;
 			}
+		}
 		info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
 		if (info == NULL) {
 			printk ("%s: Can't allocate a cdrom structure\n", drive->name);
@@ -3244,11 +2761,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	Wed May 12 13:57:12 1999
+++ linux/drivers/block/ide-cd.h	Sun Sep 12 16:39:03 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))
 
@@ -153,8 +96,7 @@
 	__u8 media_changed : 1; /* Driver has noticed a media change. */
 	__u8 toc_valid     : 1; /* Saved TOC information is current. */
 	__u8 door_locked   : 1; /* We think that the drive door is locked. */
-	__u8 sanyo_slot    : 2; /* Sanyo 3 CD changer support */
-	__u8 reserved      : 3;
+	__u8 reserved      : 5;
 	byte current_speed;	/* Current speed of the drive */
 };
 #define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags))
@@ -276,15 +218,10 @@
 };
 
 
-typedef enum {
-	mechtype_caddy = 0,
-	mechtype_tray  = 1,
-	mechtype_popup = 2,
-	mechtype_individual_changer = 4,
-	mechtype_cartridge_changer  = 5
-} 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 +455,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 +543,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 +641,108 @@
 	"(reserved)",
 };
 
-
-/* From Table 37 of the ATAPI 2.6 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. */
-
+/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) 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/ide.c linux/drivers/block/ide.c
--- /tmp/linux-2.2.12/drivers/block/ide.c	Thu Sep  9 12:27:35 1999
+++ linux/drivers/block/ide.c	Mon Sep 13 23:23:13 1999
@@ -1539,11 +1539,11 @@
 	}
 	spin_unlock_irqrestore(&io_request_lock, flags);
 	do_hwgroup_request(hwgroup);
-	save_flags(flags);	/* all CPUs; overkill? */
-	cli();			/* all CPUs; overkill? */
+	/* No need to disable interrupts, since the down() implementation
+	 * is already interrupt safe.
+	 */
 	if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
 		down(&sem);	/* wait for it to be serviced */
-	restore_flags(flags);	/* all CPUs; overkill? */
 	return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 }
 
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	Wed Nov  4 19:03:01 1998
+++ linux/drivers/block/paride/pcd.c	Sun Sep 12 16:11:51 1999
@@ -143,8 +143,8 @@
 #include <linux/delay.h>
 #include <linux/cdrom.h>
 
-#include <asm/uaccess.h>
 #include <asm/spinlock.h>
+#include <asm/uaccess.h>
 
 #ifndef MODULE
 
@@ -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	Thu Sep  9 12:27:35 1999
+++ linux/drivers/cdrom/cdrom.c	Tue Sep 14 21:44:13 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,68 @@
   -- 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.
+
+  3.04 Sep 12, 1999 - Jens Axboe <axboe@image.dk>
+  -- Fixed a couple of possible memory leaks (if an operation failed and
+  we didn't free the buffer before returning the error).
+  -- Integrated Uniform CD Changer handling from Richard Sharman
+  <rsharman@pobox.com>.
+  -- Defined CD_DVD and CD_CHANGER log levels.
+  -- Fixed the CDROMREADxxx ioctls.
+  -- CDROMPLAYTRKIND uses the GPCMD_PLAY_AUDIO_MSF command - too few
+  drives supported it. We loose the index part, however.
+  -- Small modifications to accomodate opens of /dev/hdc1, required
+  for ide-cd to handle multisession discs.
+  -- Export cdrom_mode_sense and cdrom_mode_select.
+  -- init_cdrom_command() for setting up a cgc command.
 -------------------------------------------------------------------------*/
 
-#define REVISION "Revision: 2.55"
-#define VERSION "Id: cdrom.c 2.55 1999/04/25"
+#define REVISION "Revision: 3.04"
+#define VERSION "Id: cdrom.c 3.04 1999/09/14"
 
 /* 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: */
@@ -140,6 +191,8 @@
 #define CD_OPEN		0x8
 #define CD_CLOSE	0x10
 #define CD_COUNT_TRACKS 0x20
+#define CD_CHANGER	0x40
+#define CD_DVD		0x80
 
 /* Define this to remove _all_ the debugging messages */
 /* #define ERRLOGMASK CD_NOTHING */
@@ -172,6 +225,7 @@
 static int autoclose=1;
 static int autoeject=0;
 static int lockdoor = 1;
+/* will we ever get to use this... sigh. */
 static int check_media_type = 0;
 MODULE_PARM(debug, "i");
 MODULE_PARM(autoclose, "i");
@@ -194,8 +248,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 +266,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 */ 
@@ -263,19 +327,19 @@
 	ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
 	ENSURE(lock_door, CDC_LOCK);
 	ENSURE(select_speed, CDC_SELECT_SPEED);
-	ENSURE(select_disc, CDC_SELECT_DISC);
 	ENSURE(get_last_session, CDC_MULTI_SESSION);
 	ENSURE(get_mcn, CDC_MCN);
 	ENSURE(reset, CDC_RESET);
 	ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
 	ENSURE(dev_ioctl, CDC_IOCTLS);
+	ENSURE(generic_packet, CDC_GENERIC_PACKET);
 	cdi->mc_flags = 0;
 	cdo->n_minors = 0;
         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;
@@ -325,6 +389,18 @@
 	cdi = topCdromPtr;
 	while (cdi != NULL && cdi->dev != dev)
 		cdi = cdi->next;
+
+	/* we need to find the device this way when IDE devices such
+	 * as /dev/hdc2 are opened. SCSI drives will be found above and
+	 * so will /dev/hdc, for instance.
+	 */
+	if (cdi == NULL) {
+		kdev_t cd_dev = MKDEV(MAJOR(dev), MINOR(dev) | CD_PART_MASK);
+		cdi = topCdromPtr;
+		while (cdi != NULL && cdi->dev != cd_dev)
+			cdi = cdi->next;
+	}
+	
 	return cdi;
 }
 
@@ -347,7 +423,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 +453,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 +497,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 +522,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 +536,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 +560,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);
@@ -528,7 +606,7 @@
 int cdrom_release(struct inode *ip, struct file *fp)
 {
 	kdev_t dev = ip->i_rdev;
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_device_ops *cdo = cdi->ops;
 	int opened_for_data;
 
@@ -553,13 +631,139 @@
 		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;
 }
 
+static int cdrom_read_mech_status(struct cdrom_device_info *cdi, 
+				  struct cdrom_changer_info *buf)
+{
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+	int length;
+
+	length = sizeof(struct cdrom_mechstat_header) +
+		 cdi->capacity * sizeof(struct cdrom_slot);
+
+	init_cdrom_command(&cgc, buf, length);
+	cgc.cmd[0] = GPCMD_MECHANISM_STATUS;
+	cgc.cmd[8] = (length >> 8) & 0xff;
+	cgc.cmd[9] = length & 0xff;
+	return cdo->generic_packet(cdi, &cgc);
+}
+
+static int cdrom_slot_status(struct cdrom_device_info *cdi, int slot)
+{
+	struct cdrom_changer_info info;
+	int ret;
+
+	cdinfo(CD_CHANGER, "entering cdrom_slot_status()\n"); 
+	if (cdi->sanyo_slot)
+		return CDS_NO_INFO;
+	
+	if ((ret = cdrom_read_mech_status(cdi, &info)))
+		return ret;
+
+	if (info.slots[slot].disc_present)
+		return CDS_DISC_OK;
+	else
+		return CDS_NO_DISC;
+
+}
+
+/* Return the number of slots for an ATAPI/SCSI cdrom, 
+ * return 1 if not a changer. 
+ */
+int cdrom_number_of_slots(struct cdrom_device_info *cdi) 
+{
+	int status;
+	int nslots = 1;
+	struct cdrom_changer_info info;
+
+	cdinfo(CD_CHANGER, "entering cdrom_number_of_slots()\n"); 
+	/* cdrom_read_mech_status requires a valid value for capacity: */
+	cdi->capacity = 0; 
+
+	if ((status = cdrom_read_mech_status(cdi, &info)) == 0)
+		nslots = info.hdr.nslots;
+
+	return nslots;
+}
+
+
+/* If SLOT < 0, unload the current slot.  Otherwise, try to load SLOT. */
+static int cdrom_load_unload(struct cdrom_device_info *cdi, int slot) 
+{
+	struct cdrom_generic_command cgc;
+
+	cdinfo(CD_CHANGER, "entering cdrom_load_unload()\n"); 
+	if (cdi->sanyo_slot && slot < 0)
+		return 0;
+
+	init_cdrom_command(&cgc, NULL, 0);
+	cgc.cmd[0] = GPCMD_LOAD_UNLOAD;
+	cgc.cmd[4] = 2 + (slot >= 0);
+	cgc.cmd[8] = slot;
+
+	/* The Sanyo 3 CD changer uses byte 7 of the 
+	GPCMD_TEST_UNIT_READY to command to switch CDs instead of
+	using the GPCMD_LOAD_UNLOAD opcode. */
+	if (cdi->sanyo_slot && slot) {
+		cgc.cmd[0] = GPCMD_TEST_UNIT_READY;
+		cgc.cmd[7] = slot;
+		cdi->sanyo_slot = slot ? slot : 3;
+	}
+
+	return cdi->ops->generic_packet(cdi, &cgc);
+}
+
+int cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
+{
+	struct cdrom_changer_info info;
+	int curslot;
+	int ret;
+
+	cdinfo(CD_CHANGER, "entering cdrom_select_disc()\n"); 
+	if (!CDROM_CAN(CDC_SELECT_DISC))
+		return -EDRIVE_CANT_DO_THIS;
+
+	if (slot == CDSL_NONE) {
+		/* set media changed bits, on both queues */
+		cdi->mc_flags = 0x3;
+		return cdrom_load_unload(cdi, -1);
+	}
+
+	if ((ret = cdrom_read_mech_status(cdi, &info)))
+		return ret;
+
+	curslot = info.hdr.curslot;
+
+	if (cdi->use_count > 1 || keeplocked) {
+		if (slot == CDSL_CURRENT) {
+	    		return curslot;
+		} else {
+			return -EBUSY;
+		}
+	}
+
+	/* Specifying CDSL_CURRENT will attempt to load the currnet slot,
+	which is useful if it had been previously unloaded.
+	Whether it can or not, it returns the current slot. 
+	Similarly,  if slot happens to be the current one, we still
+	try and load it. */
+	if (slot == CDSL_CURRENT)
+		slot = curslot;
+
+	/* set media changed bits on both queues */
+	cdi->mc_flags = 0x3;
+	if ((ret = cdrom_load_unload(cdi, slot)))
+		return ret;
+
+	return slot;
+}
+
 /* We want to make media_changed accessible to the user through an
  * ioctl. The main problem now is that we must double-buffer the
  * low-level implementation, to assure that the VFS and the user both
@@ -572,7 +776,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)) {
@@ -586,7 +790,7 @@
 static
 int cdrom_media_changed(kdev_t dev)
 {
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	/* This talks to the VFS, which doesn't like errors - just 1 or 0.  
 	 * Returning "0" is always safe (media hasn't been changed). Do that 
 	 * if the low-level cdrom driver dosn't support media changed. */ 
@@ -594,7 +798,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 +814,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 +888,419 @@
 	*curr = requested;
 }
 
+void init_cdrom_command(struct cdrom_generic_command *cgc,
+			void *buffer, int len)
+{
+	memset(cgc, 0, sizeof(*cgc));
+	cgc->buffer = (char *) buffer;
+	cgc->buflen = len;
+}
+
+/* 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;
+
+	init_cdrom_command(&cgc, buf, 0);
+
+	switch (ai->type) {
+	/* LU data send */
+	case DVD_LU_SEND_AGID:
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_AGID\n"); 
+		setup_report_key (&cgc, 0, 0);
+		cgc.buflen = cgc.cmd[9] = 8;
+
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
+
+		ai->lsa.agid = buf[7] >> 6;
+		/* Returning data, let host change state */
+		break;
+
+	case DVD_LU_SEND_KEY1:
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_KEY1\n"); 
+		setup_report_key (&cgc, ai->lsk.agid, 2);
+		cgc.buflen = cgc.cmd[9] = 12;
+
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
+
+		copy_key(ai->lsk.key, &buf[4]);
+		/* Returning data, let host change state */
+		break;
+
+	case DVD_LU_SEND_CHALLENGE:
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_CHALLENGE\n"); 
+		setup_report_key (&cgc, ai->lsc.agid, 1);
+		cgc.buflen = cgc.cmd[9] = 16;
+
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			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_DVD, "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;
+
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			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_DVD, "entering DVD_LU_SEND_ASF\n"); 
+		setup_report_key (&cgc, ai->lsasf.asf, 5);
+		cgc.buflen = cgc.cmd[9] = 8;
+		
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
+
+		ai->lsasf.asf = buf[7] & 1;
+		break;
+
+	/* LU data receive (LU changes state) */
+	case DVD_HOST_SEND_CHALLENGE:
+		cdinfo(CD_DVD, "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);
+
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
+
+		ai->type = DVD_LU_SEND_KEY1;
+		break;
+
+	case DVD_HOST_SEND_KEY2:
+		cdinfo(CD_DVD, "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_DVD, "entering DVD_INVALIDATE_AGID\n"); 
+		setup_report_key (&cgc, ai->lsa.agid, 0x3f);
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			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));
+	init_cdrom_command(&cgc, buf, 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;
+
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		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));
+	init_cdrom_command(&cgc, buf, 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;
+
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		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));
+	init_cdrom_command(&cgc, buf, 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;
+
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		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));
+	init_cdrom_command(&cgc, buf, sizeof(buf));
+	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+	cgc.cmd[7] = s->type;
+	cgc.cmd[9] = cgc.buflen = 0xff;
+
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		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));
+	init_cdrom_command(&cgc, buf, sizeof(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;
+
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		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;
+	}
+}
+
+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);
+}
+
+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;
+
+	init_cdrom_command(&cgc, buffer, 16);
+	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] = 16;
+
+	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[6] = (nblocks >> 16) & 0xff;
+	cgc->cmd[7] = (nblocks >>  8) & 0xff;
+	cgc->cmd[8] = nblocks & 0xff;
+	cgc->buflen = blksize * nblocks;
+	
+	/* set the header info returned */
+	switch (blksize) {
+	case CD_FRAMESIZE_RAW0	: cgc->cmd[9] = 0x58; break;
+	case CD_FRAMESIZE_RAW1	: cgc->cmd[9] = 0x78; break;
+	case CD_FRAMESIZE_RAW	: cgc->cmd[9] = 0xf8; break;
+	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
@@ -702,8 +1319,9 @@
 		unsigned int cmd, unsigned long arg)
 {
 	kdev_t dev = ip->i_rdev;
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_device_ops *cdo;
+	int ret;
 
 	if (cdi == NULL)
 		return -ENODEV;
@@ -715,7 +1333,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 +1354,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,21 +1383,28 @@
 		if (arg)
 			cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
 		return 0;
+		}
 
 	case CDROM_MEDIA_CHANGED: {
+		struct cdrom_changer_info info;
+
 		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)
+		if ((unsigned int)arg >= cdi->capacity) {
 			return -EINVAL;
-		return cdo->media_changed (cdi, arg);
 		}
 
-	case CDROM_SET_OPTIONS:
+		if ((ret = cdrom_read_mech_status(cdi, &info)))
+			return ret;
+
+		return info.slots[arg].change;
+		}
+
+	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,70 +1413,83 @@
 		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)) 
+
+                if ((arg != CDSL_CURRENT) && (arg != CDSL_NONE)) {
+		    if ((int)arg >= cdi->capacity)
+			return -EINVAL;
+		}
+		/* cdo->select_disc is a hook to allow a driver-specific
+		 * way of seleting disc.  However, since there is no
+		 * equiv hook for cdrom_slot_status this may not 
+		 * actually be useful...
+		 */
+		if (cdo->select_disc != NULL)
 			return cdo->select_disc(cdi, arg);
-		if ((int)arg >= cdi->capacity)
-			return -EDRIVE_CANT_DO_THIS;
-		return cdo->select_disc(cdi, arg);
+
+		/* no driver specific select_disc(), call our own */
+		cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n"); 
+		return cdrom_select_disc(cdi, arg);
 		}
 
 	case CDROMRESET: {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
 		cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
-		if (!(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;
-			return cdo->lock_door(cdi, arg);
-		}
+		keeplocked = arg ? 1 : 0;
+		return cdo->lock_door(cdi, arg);
 		}
 
 	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 +1498,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,11 +1513,13 @@
 		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);
+		return cdrom_slot_status(cdi, arg);
 		}
 
 	/* Ok, this is where problems start.  The current interface for the
@@ -912,7 +1550,8 @@
 		if (tracks.audio > 0) {
 			if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) 
 				return CDS_AUDIO;
-			else return CDS_MIXED;
+			else
+				return CDS_MIXED;
 		}
 		if (tracks.cdi > 0) return CDS_XA_2_2;
 		if (tracks.xa > 0) return CDS_XA_2_1;
@@ -923,239 +1562,776 @@
 		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;
+}
 
-#define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret
+static inline
+int msf_to_lba (char m, char s, char f)
+{
+	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
+}
+
+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"); */ 
-			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);
+		case CDROMREADRAW:
+			blocksize = CD_FRAMESIZE_RAW;
+			break;
+		case CDROMREADMODE1:
+			blocksize = CD_FRAMESIZE;
+			format = 2;
+			break;
+		case CDROMREADMODE2:
+			blocksize = CD_FRAMESIZE_RAW0;
+			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))
+				ret = -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)) {
+			kfree(cgc.buffer);
+			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;
+		struct cdrom_tocentry entry;
+
+		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n");
+		IOCTL_IN(arg, struct cdrom_ti, ti);
+		entry.cdte_format = CDROM_MSF;
+
+		/* get toc entry for start and end track */
+		entry.cdte_track = ti.cdti_trk0;
+		if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry))
+			return -EINVAL;
+
+		cgc.cmd[3] = entry.cdte_addr.msf.minute;
+		cgc.cmd[4] = entry.cdte_addr.msf.second;
+		cgc.cmd[5] = entry.cdte_addr.msf.frame;
+
+		entry.cdte_track = ti.cdti_trk1;
+		if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry))
+			return -EINVAL;
+
+		cgc.cmd[6] = entry.cdte_addr.msf.minute;
+		cgc.cmd[7] = entry.cdte_addr.msf.second;
+		cgc.cmd[8] = entry.cdte_addr.msf.frame;
+		cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+		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) & 0xff;
+		cgc.cmd[3] = (blk.from >> 16) & 0xff;
+		cgc.cmd[4] = (blk.from >>  8) & 0xff;
+		cgc.cmd[5] = blk.from & 0xff;
+		cgc.cmd[7] = (blk.len >> 8) & 0xff;
+		cgc.cmd[8] = blk.len & 0xff;
+		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 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 */
+		}
+		
+		/* 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)))
+			return ret;
+		IOCTL_OUT(arg, dvd_struct, s);
+		return 0;
+		}
+
+	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))
+			ret = -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;
+
+	init_cdrom_command(&cgc, ti, 8);
+	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] = 8;
+
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		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 */
+	init_cdrom_command(&cgc, di, sizeof(*di));
+	memset(di, 0, sizeof(disc_information));
+	cgc.cmd[0] = GPCMD_READ_DISC_INFO;
+	cgc.cmd[8] = cgc.buflen;
+
+	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);
 EXPORT_SYMBOL(cdrom_fops);
+EXPORT_SYMBOL(cdrom_number_of_slots);
+EXPORT_SYMBOL(cdrom_select_disc);
+EXPORT_SYMBOL(cdrom_mode_select);
+EXPORT_SYMBOL(cdrom_mode_sense);
+EXPORT_SYMBOL(init_cdrom_command);
 
 #ifdef CONFIG_SYSCTL
 
 #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(info+pos, "\t%s", cdi->name);
+
+	pos += sprintf(info+pos, "\ndrive speed:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t%d", cdi->speed);
+
+	pos += sprintf(info+pos, "\ndrive # of slots:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(info+pos, "\t%d", cdi->capacity);
+
+	pos += sprintf(info+pos, "\nCan close tray:\t");
 	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
-	    pos += sprintf(cdrom_drive_info+pos, "\t%s", cdi->name);
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CLOSE_TRAY) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\ndrive speed:\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->speed);
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_OPEN_TRAY) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\ndrive # of slots:");
+	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->capacity);
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_LOCK) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan close tray:\t");
+	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_CLOSE_TRAY)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_SPEED) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan open tray:\t");
+	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_OPEN_TRAY)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_DISC) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan lock tray:\t");
+	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 & ~cdi->mask & CDC_LOCK)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MULTI_SESSION) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan change speed:");
+	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 & ~cdi->mask & CDC_SELECT_SPEED)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MCN) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan select disk:");
+	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 & ~cdi->mask & CDC_SELECT_DISC)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MEDIA_CHANGED) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan read multisession:");
+	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 & CDC_MULTI_SESSION)!=0));
+	    pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_PLAY_AUDIO) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan read MCN:\t");
+	pos += sprintf(info+pos, "\nCan write CD-R:\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_CD_R) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nReports media changed:");
+	pos += sprintf(info+pos, "\nCan write CD-RW:");
 	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_CD_RW) != 0);
 
-	pos += sprintf(cdrom_drive_info+pos, "\nCan play audio:\t");
+	pos += sprintf(info+pos, "\nCan read DVD:\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_DVD) != 0);
 
-        strcpy(cdrom_drive_info+pos,"\n\n");
-	pos += 3;
-	if (*lenp > pos)
-		*lenp = pos;
+	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 +2375,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 +2412,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	Fri Jan 15 23:41:04 1999
+++ linux/drivers/scsi/sr.c	Sun Sep 12 16:13:33 1999
@@ -1,25 +1,28 @@
 /*
  *  sr.c Copyright (C) 1992 David Giller
- *	     Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *           Copyright (C) 1993, 1994, 1995 Eric Youngdale
  *
  *  adapted from:
- *	sd.c Copyright (C) 1992 Drew Eckhardt
- *	Linux scsi disk driver by
- *		Drew Eckhardt <drew@colorado.edu>
+ *      sd.c Copyright (C) 1992 Drew Eckhardt
+ *      Linux scsi disk driver by
+ *              Drew Eckhardt <drew@colorado.edu>
  *
  *      Modified by Eric Youngdale ericy@cais.com to
  *      add scatter-gather, multiple outstanding request, and other
  *      enhancements.
  *
- *	    Modified by Eric Youngdale eric@aib.com to support loadable
- *	    low-level scsi drivers.
+ *          Modified by Eric Youngdale eric@aib.com to support loadable
+ *          low-level scsi drivers.
  *
- *	 Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
- *	 provide auto-eject.
+ *       Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
+ *       provide auto-eject.
  *
  *          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,19 +37,20 @@
 #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>
 #include "scsi.h"
 #include "hosts.h"
 #include "sr.h"
-#include <scsi/scsi_ioctl.h>   /* For the door lock/unlock commands */
+#include <scsi/scsi_ioctl.h>	/* For the door lock/unlock commands */
 #include "constants.h"
 
-MODULE_PARM(xa_test,"i"); /* see sr_ioctl.c */
+MODULE_PARM(xa_test, "i");	/* see sr_ioctl.c */
 
-#define MAX_RETRIES 3
-#define SR_TIMEOUT (30 * HZ)
+#define MAX_RETRIES	3
+#define SR_TIMEOUT	(30 * HZ)
 
 static int sr_init(void);
 static void sr_finish(void);
@@ -54,53 +58,60 @@
 static int sr_detect(Scsi_Device *);
 static void sr_detach(Scsi_Device *);
 
-struct Scsi_Device_Template sr_template = {NULL, "cdrom", "sr", NULL, TYPE_ROM,
-                                           SCSI_CDROM_MAJOR, 0, 0, 0, 1,
-                                           sr_detect, sr_init,
-                                           sr_finish, sr_attach, sr_detach};
+struct Scsi_Device_Template sr_template = {
+	NULL, "cdrom", "sr", NULL, TYPE_ROM,
+	SCSI_CDROM_MAJOR, 0, 0, 0, 1,
+	sr_detect, sr_init,
+	sr_finish, sr_attach, sr_detach
+};
 
-Scsi_CD * scsi_CDs = NULL;
-static int * sr_sizes = NULL;
+Scsi_CD *scsi_CDs = NULL;
+static int *sr_sizes = NULL;
 
-static int * sr_blocksizes = NULL;
+static int *sr_blocksizes = NULL;
 
-static int sr_open(struct cdrom_device_info*, int);
+static int sr_open(struct cdrom_device_info *, int);
 void get_sectorsize(int);
 void get_capabilities(int);
 
-void requeue_sr_request (Scsi_Cmnd * SCpnt);
-static int sr_media_change(struct cdrom_device_info*, int);
+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)
 {
 	if (scsi_CDs[MINOR(cdi->dev)].sector_size > 2048)
-		sr_set_blocklength(MINOR(cdi->dev),2048);
+		sr_set_blocklength(MINOR(cdi->dev), 2048);
 	sync_dev(cdi->dev);
 	scsi_CDs[MINOR(cdi->dev)].device->access_count--;
 	if (scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module)
 		__MOD_DEC_USE_COUNT(scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module);
-	if(sr_template.module)
-        	__MOD_DEC_USE_COUNT(sr_template.module);
+	if (sr_template.module)
+		__MOD_DEC_USE_COUNT(sr_template.module);
 }
 
-static struct cdrom_device_ops sr_dops = {
-        sr_open,                      /* open */
-        sr_release,                   /* release */
-        sr_drive_status,              /* drive status */
-        sr_media_change,              /* media changed */
-        sr_tray_move,                 /* tray move */
-        sr_lock_door,                 /* lock door */
-        sr_select_speed,              /* select speed */
-        NULL,                         /* select disc */
-        sr_get_last_session,          /* get last session */
-        sr_get_mcn,                   /* get universal product code */
-        sr_reset,                     /* hard reset */
-        sr_audio_ioctl,               /* audio ioctl */
-        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
+static struct cdrom_device_ops sr_dops =
+{
+	sr_open,		/* open */
+	sr_release,		/* release */
+	sr_drive_status,	/* drive status */
+	sr_media_change,	/* media changed */
+	sr_tray_move,		/* tray move */
+	sr_lock_door,		/* lock door */
+	sr_select_speed,	/* select speed */
+	NULL,			/* select disc */
+	sr_get_last_session,	/* get last session */
+	sr_get_mcn,		/* get universal product code */
+	sr_reset,		/* hard reset */
+	sr_audio_ioctl,		/* audio ioctl */
+	sr_dev_ioctl,		/* device-specific 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 |
+	CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM |
+	CDC_GENERIC_PACKET,
+	0,
+	sr_packet
 };
 
 /*
@@ -113,48 +124,47 @@
  * an inode for that to work, and we do not always have one.
  */
 
-int sr_media_change(struct cdrom_device_info *cdi, int slot){
+int sr_media_change(struct cdrom_device_info *cdi, int slot)
+{
 	int retval;
 
-        if (CDSL_CURRENT != slot) {
-                /* no changer support */
-                return -EINVAL;
-        }
-
+	if (CDSL_CURRENT != slot) {
+		/* no changer support */
+		return -EINVAL;
+	}
 	retval = scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device,
-                            SCSI_IOCTL_TEST_UNIT_READY, 0);
+			    SCSI_IOCTL_TEST_UNIT_READY, 0);
 
-	if(retval)
-        {
-                /* Unable to test, unit probably not ready.  This usually
+	if (retval) {
+		/* Unable to test, unit probably not ready.  This usually
 		 * means there is no disc in the drive.  Mark as changed,
 		 * and we will figure it out later once the drive is
 		 * available again.  */
 
-                scsi_CDs[MINOR(cdi->dev)].device->changed = 1;
-                return 1; /* This will force a flush, if called from
-                           * check_disk_change */
+		scsi_CDs[MINOR(cdi->dev)].device->changed = 1;
+		return 1;	/* This will force a flush, if called from
+				 * check_disk_change */
 	};
 
 	retval = scsi_CDs[MINOR(cdi->dev)].device->changed;
-        scsi_CDs[MINOR(cdi->dev)].device->changed = 0;
-        /* If the disk changed, the capacity will now be different,
-         * so we force a re-read of this information */
-        if (retval) {
+	scsi_CDs[MINOR(cdi->dev)].device->changed = 0;
+	/* If the disk changed, the capacity will now be different,
+	 * so we force a re-read of this information */
+	if (retval) {
 		/* check multisession offset etc */
-                sr_cd_check(cdi);
-		
-                 /* 
-                  * If the disk changed, the capacity will now be different,
-                  * so we force a re-read of this information 
-                  * Force 2048 for the sector size so that filesystems won't
-                  * be trying to use something that is too small if the disc
-                  * has changed.
-                  */
-                scsi_CDs[MINOR(cdi->dev)].needs_sector_size = 1;
+		sr_cd_check(cdi);
 
-                scsi_CDs[MINOR(cdi->dev)].sector_size = 2048;
-        }
+		/* 
+		 * If the disk changed, the capacity will now be different,
+		 * so we force a re-read of this information 
+		 * Force 2048 for the sector size so that filesystems won't
+		 * be trying to use something that is too small if the disc
+		 * has changed.
+		 */
+		scsi_CDs[MINOR(cdi->dev)].needs_sector_size = 1;
+
+		scsi_CDs[MINOR(cdi->dev)].sector_size = 2048;
+	}
 	return retval;
 }
 
@@ -163,7 +173,7 @@
  * end of a SCSI read / write, and will take on of several actions based on success or failure.
  */
 
-static void rw_intr (Scsi_Cmnd * SCpnt)
+static void rw_intr(Scsi_Cmnd * SCpnt)
 {
 	int result = SCpnt->result;
 	int this_count = SCpnt->this_count;
@@ -171,137 +181,130 @@
 	int block_sectors = 0;
 
 #ifdef DEBUG
-	printk("sr.c done: %x %x\n",result, SCpnt->request.bh->b_data);
+	printk("sr.c done: %x %x\n", result, SCpnt->request.bh->b_data);
 #endif
-    /*
-      Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial success.
-      Since this is a relatively rare error condition, no care is taken to
-      avoid unnecessary additional work such as memcpy's that could be avoided.
-    */
+	/*
+	   Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial success.
+	   Since this is a relatively rare error condition, no care is taken to
+	   avoid unnecessary additional work such as memcpy's that could be avoided.
+	 */
 
-	if (driver_byte(result) != 0 &&		    /* An error occurred */
-	    SCpnt->sense_buffer[0] == 0xF0 &&	    /* Sense data is valid */
+	if (driver_byte(result) != 0 &&		/* An error occurred */
+	    SCpnt->sense_buffer[0] == 0xF0 &&	/* Sense data is valid */
 	    (SCpnt->sense_buffer[2] == MEDIUM_ERROR ||
 	     SCpnt->sense_buffer[2] == VOLUME_OVERFLOW ||
-	     SCpnt->sense_buffer[2] == ILLEGAL_REQUEST))
-	  {
-	    long error_sector = (SCpnt->sense_buffer[3] << 24) |
-				(SCpnt->sense_buffer[4] << 16) |
-				(SCpnt->sense_buffer[5] << 8) |
-				SCpnt->sense_buffer[6];
-	    int device_nr = DEVICE_NR(SCpnt->request.rq_dev);
-	    if (SCpnt->request.bh != NULL)
-	      block_sectors = SCpnt->request.bh->b_size >> 9;
-	    if (block_sectors < 4) block_sectors = 4;
-	    if (scsi_CDs[device_nr].sector_size == 2048)
-	      error_sector <<= 2;
-	    error_sector &= ~ (block_sectors - 1);
-	    good_sectors = error_sector - SCpnt->request.sector;
-	    if (good_sectors < 0 || good_sectors >= this_count)
-	      good_sectors = 0;
-	    /*
-	      The SCSI specification allows for the value returned by READ
-	      CAPACITY to be up to 75 2K sectors past the last readable
-	      block.  Therefore, if we hit a medium error within the last
-	      75 2K sectors, we decrease the saved size value.
-	    */
-	    if ((error_sector >> 1) < sr_sizes[device_nr] &&
-		scsi_CDs[device_nr].capacity - error_sector < 4*75)
-	      sr_sizes[device_nr] = error_sector >> 1;
-	  }
-
-	if (good_sectors > 0)
-    { /* Some sectors were read successfully. */
-	if (SCpnt->use_sg == 0) {
-		    if (SCpnt->buffer != SCpnt->request.buffer)
-	    {
-		int offset;
-		offset = (SCpnt->request.sector % 4) << 9;
-		memcpy((char *)SCpnt->request.buffer,
-		       (char *)SCpnt->buffer + offset,
-		       good_sectors << 9);
-		/* Even though we are not using scatter-gather, we look
-		 * ahead and see if there is a linked request for the
-		 * other half of this buffer.  If there is, then satisfy
-		 * it. */
-		if((offset == 0) && good_sectors == 2 &&
-		   SCpnt->request.nr_sectors > good_sectors &&
-		   SCpnt->request.bh &&
-		   SCpnt->request.bh->b_reqnext &&
-		   SCpnt->request.bh->b_reqnext->b_size == 1024) {
-		    memcpy((char *)SCpnt->request.bh->b_reqnext->b_data,
-			   (char *)SCpnt->buffer + 1024,
-			   1024);
-		    good_sectors += 2;
-		};
+	     SCpnt->sense_buffer[2] == ILLEGAL_REQUEST)) {
+		long error_sector = (SCpnt->sense_buffer[3] << 24) |
+		(SCpnt->sense_buffer[4] << 16) |
+		(SCpnt->sense_buffer[5] << 8) |
+		SCpnt->sense_buffer[6];
+		int device_nr = DEVICE_NR(SCpnt->request.rq_dev);
+		if (SCpnt->request.bh != NULL)
+			block_sectors = SCpnt->request.bh->b_size >> 9;
+		if (block_sectors < 4)
+			block_sectors = 4;
+		if (scsi_CDs[device_nr].sector_size == 2048)
+			error_sector <<= 2;
+		error_sector &= ~(block_sectors - 1);
+		good_sectors = error_sector - SCpnt->request.sector;
+		if (good_sectors < 0 || good_sectors >= this_count)
+			good_sectors = 0;
+		/*
+		   The SCSI specification allows for the value returned by READ
+		   CAPACITY to be up to 75 2K sectors past the last readable
+		   block.  Therefore, if we hit a medium error within the last
+		   75 2K sectors, we decrease the saved size value.
+		 */
+		if ((error_sector >> 1) < sr_sizes[device_nr] &&
+		    scsi_CDs[device_nr].capacity - error_sector < 4 * 75)
+			sr_sizes[device_nr] = error_sector >> 1;
+	}
+	if (good_sectors > 0) {	/* Some sectors were read successfully. */
+		if (SCpnt->use_sg == 0) {
+			if (SCpnt->buffer != SCpnt->request.buffer) {
+				int offset;
+				offset = (SCpnt->request.sector % 4) << 9;
+				memcpy((char *) SCpnt->request.buffer,
+				       (char *) SCpnt->buffer + offset,
+				       good_sectors << 9);
+				/* Even though we are not using scatter-gather, we look
+				 * ahead and see if there is a linked request for the
+				 * other half of this buffer.  If there is, then satisfy
+				 * it. */
+				if ((offset == 0) && good_sectors == 2 &&
+				    SCpnt->request.nr_sectors > good_sectors &&
+				    SCpnt->request.bh &&
+				    SCpnt->request.bh->b_reqnext &&
+				    SCpnt->request.bh->b_reqnext->b_size == 1024) {
+					memcpy((char *) SCpnt->request.bh->b_reqnext->b_data,
+					   (char *) SCpnt->buffer + 1024,
+					       1024);
+					good_sectors += 2;
+				};
 
-		scsi_free(SCpnt->buffer, 2048);
-	    }
-	} else {
-		    struct scatterlist * sgpnt;
-		    int i;
-		    sgpnt = (struct scatterlist *) SCpnt->buffer;
-		    for(i=0; i<SCpnt->use_sg; i++) {
-		if (sgpnt[i].alt_address) {
-		    if (sgpnt[i].alt_address != sgpnt[i].address) {
-			memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
-		    };
-		    scsi_free(sgpnt[i].address, sgpnt[i].length);
+				scsi_free(SCpnt->buffer, 2048);
+			}
+		} else {
+			struct scatterlist *sgpnt;
+			int i;
+			sgpnt = (struct scatterlist *) SCpnt->buffer;
+			for (i = 0; i < SCpnt->use_sg; i++) {
+				if (sgpnt[i].alt_address) {
+					if (sgpnt[i].alt_address != sgpnt[i].address) {
+						memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+					};
+					scsi_free(sgpnt[i].address, sgpnt[i].length);
+				};
+			};
+			scsi_free(SCpnt->buffer, SCpnt->sglist_len);	/* Free list of scatter-gather pointers */
+			if (SCpnt->request.sector % 4)
+				good_sectors -= 2;
+			/* See   if there is a padding record at the end that needs to be removed */
+			if (good_sectors > SCpnt->request.nr_sectors)
+				good_sectors -= 2;
 		};
-		    };
-		    scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */
-		    if(SCpnt->request.sector % 4) good_sectors -= 2;
-	    /* See   if there is a padding record at the end that needs to be removed */
-		    if(good_sectors > SCpnt->request.nr_sectors)
-		good_sectors -= 2;
-	};
 
 #ifdef DEBUG
-		printk("(%x %x %x) ",SCpnt->request.bh, SCpnt->request.nr_sectors,
+		printk("(%x %x %x) ", SCpnt->request.bh, SCpnt->request.nr_sectors,
 		       good_sectors);
 #endif
-		if (SCpnt->request.nr_sectors > this_count)
-	{
+		if (SCpnt->request.nr_sectors > this_count) {
 			SCpnt->request.errors = 0;
 			if (!SCpnt->request.bh)
-			    panic("sr.c: linked page request (%lx %x)",
-		      SCpnt->request.sector, this_count);
-	}
-
-	SCpnt = end_scsi_request(SCpnt, 1, good_sectors);  /* All done */
-	if (result == 0)
-	  {
-	    requeue_sr_request(SCpnt);
-	    return;
-	  }
-    }
-
-    if (good_sectors == 0) {
-	/* We only come through here if no sectors were read successfully. */
-
-    /* Free up any indirection buffers we allocated for DMA purposes. */
-	if (SCpnt->use_sg) {
-	struct scatterlist * sgpnt;
-	int i;
-	sgpnt = (struct scatterlist *) SCpnt->buffer;
-	for(i=0; i<SCpnt->use_sg; i++) {
-	    if (sgpnt[i].alt_address) {
-		scsi_free(sgpnt[i].address, sgpnt[i].length);
-	    }
-	}
-	scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */
-	} else {
-	if (SCpnt->buffer != SCpnt->request.buffer)
-	    scsi_free(SCpnt->buffer, SCpnt->bufflen);
+				panic("sr.c: linked page request (%lx %x)",
+				      SCpnt->request.sector, this_count);
+		}
+		SCpnt = end_scsi_request(SCpnt, 1, good_sectors);	/* All done */
+		if (result == 0) {
+			requeue_sr_request(SCpnt);
+			return;
+		}
 	}
+	if (good_sectors == 0) {
+		/* We only come through here if no sectors were read successfully. */
 
-    }
+		/* Free up any indirection buffers we allocated for DMA purposes. */
+		if (SCpnt->use_sg) {
+			struct scatterlist *sgpnt;
+			int i;
+			sgpnt = (struct scatterlist *) SCpnt->buffer;
+			for (i = 0; i < SCpnt->use_sg; i++) {
+				if (sgpnt[i].alt_address) {
+					scsi_free(sgpnt[i].address, sgpnt[i].length);
+				}
+			}
+			scsi_free(SCpnt->buffer, SCpnt->sglist_len);	/* Free list of scatter-gather pointers */
+		} else {
+			if (SCpnt->buffer != SCpnt->request.buffer)
+				scsi_free(SCpnt->buffer, SCpnt->bufflen);
+		}
 
+	}
 	if (driver_byte(result) != 0) {
 		if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
 			if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
 				/* detected disc change.  set a bit and quietly refuse
-				 * further access.	*/
+				 * further access.    */
 
 				scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;
 				SCpnt = end_scsi_request(SCpnt, 0, this_count);
@@ -309,10 +312,9 @@
 				return;
 			}
 		}
-
 		if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
 			printk("sr%d: CD-ROM error: ",
-                               DEVICE_NR(SCpnt->request.rq_dev));
+			       DEVICE_NR(SCpnt->request.rq_dev));
 			print_sense("sr", SCpnt);
 			printk("command was: ");
 			print_command(SCpnt->cmnd);
@@ -323,95 +325,87 @@
 				return;
 			} else {
 				SCpnt = end_scsi_request(SCpnt, 0, this_count);
-				requeue_sr_request(SCpnt); /* Do next request */
+				requeue_sr_request(SCpnt);	/* Do next request */
 				return;
 			}
 
 		}
-
 		if (SCpnt->sense_buffer[2] == NOT_READY) {
 			printk(KERN_INFO "sr%d: CD-ROM not ready.  Make sure you have a disc in the drive.\n",
-                               DEVICE_NR(SCpnt->request.rq_dev));
+			       DEVICE_NR(SCpnt->request.rq_dev));
 			SCpnt = end_scsi_request(SCpnt, 0, this_count);
-			requeue_sr_request(SCpnt); /* Do next request */
+			requeue_sr_request(SCpnt);	/* Do next request */
 			return;
 		}
-
 		if (SCpnt->sense_buffer[2] == MEDIUM_ERROR) {
-		    printk("scsi%d: MEDIUM ERROR on "
-			   "channel %d, id %d, lun %d, CDB: ",
-			   SCpnt->host->host_no, (int) SCpnt->channel,
-			   (int) SCpnt->target, (int) SCpnt->lun);
-		    print_command(SCpnt->cmnd);
-		    print_sense("sr", SCpnt);
-		    SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
-		    requeue_sr_request(SCpnt);
-		    return;
+			printk("scsi%d: MEDIUM ERROR on "
+			       "channel %d, id %d, lun %d, CDB: ",
+			       SCpnt->host->host_no, (int) SCpnt->channel,
+			       (int) SCpnt->target, (int) SCpnt->lun);
+			print_command(SCpnt->cmnd);
+			print_sense("sr", SCpnt);
+			SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+			requeue_sr_request(SCpnt);
+			return;
 		}
-
 		if (SCpnt->sense_buffer[2] == VOLUME_OVERFLOW) {
-		    printk("scsi%d: VOLUME OVERFLOW on "
-			   "channel %d, id %d, lun %d, CDB: ",
-			   SCpnt->host->host_no, (int) SCpnt->channel,
-			   (int) SCpnt->target, (int) SCpnt->lun);
-		    print_command(SCpnt->cmnd);
-		    print_sense("sr", SCpnt);
-		    SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
-		    requeue_sr_request(SCpnt);
-		    return;
+			printk("scsi%d: VOLUME OVERFLOW on "
+			       "channel %d, id %d, lun %d, CDB: ",
+			       SCpnt->host->host_no, (int) SCpnt->channel,
+			       (int) SCpnt->target, (int) SCpnt->lun);
+			print_command(SCpnt->cmnd);
+			print_sense("sr", SCpnt);
+			SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+			requeue_sr_request(SCpnt);
+			return;
 		}
-        }
-
+	}
 	/* We only get this far if we have an error we have not recognized */
-	if(result) {
-	printk("SCSI CD error : host %d id %d lun %d return code = %03x\n",
-	       scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
-	       scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
-	       scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->lun,
-	       result);
-
-	if (status_byte(result) == CHECK_CONDITION)
-	    print_sense("sr", SCpnt);
-
-	SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
-	requeue_sr_request(SCpnt);
-    }
+	if (result) {
+		printk("SCSI CD error : host %d id %d lun %d return code = %03x\n",
+		       scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
+		   scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
+		  scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->lun,
+		       result);
+
+		if (status_byte(result) == CHECK_CONDITION)
+			print_sense("sr", SCpnt);
+
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+		requeue_sr_request(SCpnt);
+	}
 }
 
 static int sr_open(struct cdrom_device_info *cdi, int purpose)
 {
-    check_disk_change(cdi->dev);
+	check_disk_change(cdi->dev);
 
-    if(   MINOR(cdi->dev) >= sr_template.dev_max 
-       || !scsi_CDs[MINOR(cdi->dev)].device)
-      {
-	return -ENXIO;   /* No such device */
-      }
-
-    /*
-     * If the device is in error recovery, wait until it is done.
-     * If the device is offline, then disallow any access to it.
-     */
-    if( !scsi_block_when_processing_errors(scsi_CDs[MINOR(cdi->dev)].device) )
-      {
-        return -ENXIO;
-      }
-
-    scsi_CDs[MINOR(cdi->dev)].device->access_count++;
-    if (scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module)
-	__MOD_INC_USE_COUNT(scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module);
-    if(sr_template.module)
-        __MOD_INC_USE_COUNT(sr_template.module);
-
-    /* If this device did not have media in the drive at boot time, then
-     * we would have been unable to get the sector size.  Check to see if
-     * this is the case, and try again.
-     */
+	if (MINOR(cdi->dev) >= sr_template.dev_max
+	    || !scsi_CDs[MINOR(cdi->dev)].device) {
+		return -ENXIO;	/* No such device */
+	}
+	/*
+	 * If the device is in error recovery, wait until it is done.
+	 * If the device is offline, then disallow any access to it.
+	 */
+	if (!scsi_block_when_processing_errors(scsi_CDs[MINOR(cdi->dev)].device)) {
+		return -ENXIO;
+	}
+	scsi_CDs[MINOR(cdi->dev)].device->access_count++;
+	if (scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module)
+		__MOD_INC_USE_COUNT(scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module);
+	if (sr_template.module)
+		__MOD_INC_USE_COUNT(sr_template.module);
+
+	/* If this device did not have media in the drive at boot time, then
+	 * we would have been unable to get the sector size.  Check to see if
+	 * this is the case, and try again.
+	 */
 
-    if(scsi_CDs[MINOR(cdi->dev)].needs_sector_size)
-	get_sectorsize(MINOR(cdi->dev));
+	if (scsi_CDs[MINOR(cdi->dev)].needs_sector_size)
+		get_sectorsize(MINOR(cdi->dev));
 
-    return 0;
+	return 0;
 }
 
 /*
@@ -420,111 +414,106 @@
  * translate them to SCSI commands.
  */
 
-static void do_sr_request (void)
+static void do_sr_request(void)
 {
-    Scsi_Cmnd * SCpnt = NULL;
-    struct request * req = NULL;
-    Scsi_Device * SDev;
-    int flag = 0;
-
-    while (1==1){
-	if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
-	    return;
-	};
+	Scsi_Cmnd *SCpnt = NULL;
+	struct request *req = NULL;
+	Scsi_Device *SDev;
+	int flag = 0;
 
-	INIT_SCSI_REQUEST;
+	while (1 == 1) {
+		if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
+			return;
+		};
 
-	SDev = scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device;
+		INIT_SCSI_REQUEST;
 
-        /*
-         * If the host for this device is in error recovery mode, don't
-         * do anything at all here.  When the host leaves error recovery
-         * mode, it will automatically restart things and start queueing
-         * commands again.
-         */
-        if( SDev->host->in_recovery )
-          {
-            return;
-          }
+		SDev = scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device;
 
-	/*
-	 * I am not sure where the best place to do this is.  We need
-	 * to hook in a place where we are likely to come if in user
-	 * space.
-	 */
-	if( SDev->was_reset )
-	{
- 	    /*
- 	     * We need to relock the door, but we might
- 	     * be in an interrupt handler.  Only do this
- 	     * from user space, since we do not want to
- 	     * sleep from an interrupt.
- 	     */
- 	    if( SDev->removable && !in_interrupt() )
- 	    {
-		spin_unlock_irq(&io_request_lock);		/* FIXME!!!! */
-		scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
-		spin_lock_irq(&io_request_lock);		/* FIXME!!!! */
-		/* scsi_ioctl may allow CURRENT to change, so start over. */
-		SDev->was_reset = 0;
-		continue;
- 	    }
- 	    SDev->was_reset = 0;
-	}
-
-	/* we do lazy blocksize switching (when reading XA sectors,
-	 * see CDROMREADMODE2 ioctl) */
-	if (scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].sector_size > 2048) {
-	    if (!in_interrupt())
-		sr_set_blocklength(DEVICE_NR(CURRENT->rq_dev),2048);
+		/*
+		 * If the host for this device is in error recovery mode, don't
+		 * do anything at all here.  When the host leaves error recovery
+		 * mode, it will automatically restart things and start queueing
+		 * commands again.
+		 */
+		if (SDev->host->in_recovery) {
+			return;
+		}
+		/*
+		 * I am not sure where the best place to do this is.  We need
+		 * to hook in a place where we are likely to come if in user
+		 * space.
+		 */
+		if (SDev->was_reset) {
+			/*
+			 * We need to relock the door, but we might
+			 * be in an interrupt handler.  Only do this
+			 * from user space, since we do not want to
+			 * sleep from an interrupt.
+			 */
+			if (SDev->removable && !in_interrupt()) {
+				spin_unlock_irq(&io_request_lock);	/* FIXME!!!! */
+				scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+				spin_lock_irq(&io_request_lock);	/* FIXME!!!! */
+				/* scsi_ioctl may allow CURRENT to change, so start over. */
+				SDev->was_reset = 0;
+				continue;
+			}
+			SDev->was_reset = 0;
+		}
+		/* we do lazy blocksize switching (when reading XA sectors,
+		 * see CDROMREADMODE2 ioctl) */
+		if (scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].sector_size > 2048) {
+			if (!in_interrupt())
+				sr_set_blocklength(DEVICE_NR(CURRENT->rq_dev), 2048);
 #if 1
-            else
-                printk("sr: can't switch blocksize: in interrupt\n");
+			else
+				printk("sr: can't switch blocksize: in interrupt\n");
 #endif
-	}
-
-	if (flag++ == 0)
-	    SCpnt = scsi_allocate_device(&CURRENT,
-				    scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
-	else SCpnt = NULL;
-
-	/* This is a performance enhancement.  We dig down into the request list and
-	 * try to find a queueable request (i.e. device not busy, and host able to
-	 * accept another command.  If we find one, then we queue it. This can
-	 * make a big difference on systems with more than one disk drive.  We want
-	 * to have the interrupts off when monkeying with the request list, because
-	 * otherwise the kernel might try to slip in a request in between somewhere. */
-
-	if (!SCpnt && sr_template.nr_dev > 1){
-	    struct request *req1;
-	    req1 = NULL;
-	    req = CURRENT;
-	    while(req){
-		SCpnt = scsi_request_queueable(req,
-					  scsi_CDs[DEVICE_NR(req->rq_dev)].device);
-		if(SCpnt) break;
-		req1 = req;
-		req = req->next;
-	    }
-	    if (SCpnt && req->rq_status == RQ_INACTIVE) {
-		if (req == CURRENT)
-		    CURRENT = CURRENT->next;
+		}
+		if (flag++ == 0)
+			SCpnt = scsi_allocate_device(&CURRENT,
+			 scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
 		else
-		    req1->next = req->next;
-	    }
-	}
+			SCpnt = NULL;
 
-	if (!SCpnt)
-	    return; /* Could not find anything to do */
+		/* This is a performance enhancement.  We dig down into the request list and
+		 * try to find a queueable request (i.e. device not busy, and host able to
+		 * accept another command.  If we find one, then we queue it. This can
+		 * make a big difference on systems with more than one disk drive.  We want
+		 * to have the interrupts off when monkeying with the request list, because
+		 * otherwise the kernel might try to slip in a request in between somewhere. */
+
+		if (!SCpnt && sr_template.nr_dev > 1) {
+			struct request *req1;
+			req1 = NULL;
+			req = CURRENT;
+			while (req) {
+				SCpnt = scsi_request_queueable(req,
+				scsi_CDs[DEVICE_NR(req->rq_dev)].device);
+				if (SCpnt)
+					break;
+				req1 = req;
+				req = req->next;
+			}
+			if (SCpnt && req->rq_status == RQ_INACTIVE) {
+				if (req == CURRENT)
+					CURRENT = CURRENT->next;
+				else
+					req1->next = req->next;
+			}
+		}
+		if (!SCpnt)
+			return;	/* Could not find anything to do */
 
-	wake_up(&wait_for_request);
+		wake_up(&wait_for_request);
 
-	/* Queue command */
-	requeue_sr_request(SCpnt);
-    }  /* While */
+		/* Queue command */
+		requeue_sr_request(SCpnt);
+	}			/* While */
 }
 
-void requeue_sr_request (Scsi_Cmnd * SCpnt)
+void requeue_sr_request(Scsi_Cmnd * SCpnt)
 {
 	unsigned int dev, block, realcount;
 	unsigned char cmd[10], *buffer, tries;
@@ -532,244 +521,234 @@
 
 	tries = 2;
 
- repeat:
-	if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
+repeat:
+	if (!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
 		do_sr_request();
 		return;
 	}
-
-	dev =  MINOR(SCpnt->request.rq_dev);
+	dev = MINOR(SCpnt->request.rq_dev);
 	block = SCpnt->request.sector;
 	buffer = NULL;
 	this_count = 0;
 
 	if (dev >= sr_template.nr_dev) {
-		/* printk("CD-ROM request error: invalid device.\n");			*/
+		/* printk("CD-ROM request error: invalid device.\n");                   */
 		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
 		tries = 2;
 		goto repeat;
 	}
-
 	if (!scsi_CDs[dev].use) {
-		/* printk("CD-ROM request error: device marked not in use.\n");		*/
+		/* printk("CD-ROM request error: device marked not in use.\n");         */
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		tries = 2;
+		goto repeat;
+	}
+	if (!scsi_CDs[dev].device->online) {
 		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
 		tries = 2;
 		goto repeat;
 	}
-
-	if( !scsi_CDs[dev].device->online )
-          {
-            SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
-            tries = 2;
-            goto repeat;
-          }
-
 	if (scsi_CDs[dev].device->changed) {
-	/*
-	 * quietly refuse to do anything to a changed disc
-	 * until the changed bit has been reset
-	 */
-		/* printk("CD-ROM has been changed.  Prohibiting further I/O.\n");	*/
+		/*
+		 * quietly refuse to do anything to a changed disc
+		 * until the changed bit has been reset
+		 */
+		/* printk("CD-ROM has been changed.  Prohibiting further I/O.\n");      */
 		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
 		tries = 2;
 		goto repeat;
 	}
-
-	switch (SCpnt->request.cmd)
-    {
-    case WRITE:
-	SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
-	goto repeat;
-	break;
-    case READ :
-	cmd[0] = READ_6;
-	break;
-    default :
-	panic ("Unknown sr command %d\n", SCpnt->request.cmd);
-    }
+	switch (SCpnt->request.cmd) {
+	case WRITE:
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		goto repeat;
+		break;
+	case READ:
+		cmd[0] = READ_6;
+		break;
+	default:
+		panic("Unknown sr command %d\n", SCpnt->request.cmd);
+	}
 
 	cmd[1] = (SCpnt->lun << 5) & 0xe0;
 
-    /*
-     * Now do the grungy work of figuring out which sectors we need, and
-     * where in memory we are going to put them.
-     *
-     * The variables we need are:
-     *
-     * this_count= number of 512 byte sectors being read
-     * block     = starting cdrom sector to read.
-     * realcount = # of cdrom sectors to read
-     *
-     * The major difference between a scsi disk and a scsi cdrom
-     * is that we will always use scatter-gather if we can, because we can
-     * work around the fact that the buffer cache has a block size of 1024,
-     * and we have 2048 byte sectors.  This code should work for buffers that
-     * are any multiple of 512 bytes long.
-     */
+	/*
+	 * Now do the grungy work of figuring out which sectors we need, and
+	 * where in memory we are going to put them.
+	 *
+	 * The variables we need are:
+	 *
+	 * this_count= number of 512 byte sectors being read
+	 * block     = starting cdrom sector to read.
+	 * realcount = # of cdrom sectors to read
+	 *
+	 * The major difference between a scsi disk and a scsi cdrom
+	 * is that we will always use scatter-gather if we can, because we can
+	 * work around the fact that the buffer cache has a block size of 1024,
+	 * and we have 2048 byte sectors.  This code should work for buffers that
+	 * are any multiple of 512 bytes long.
+	 */
 
 	SCpnt->use_sg = 0;
 
 	if (SCpnt->host->sg_tablesize > 0 &&
 	    (!scsi_need_isa_buffer ||
-	 scsi_dma_free_sectors >= 10)) {
-	struct buffer_head * bh;
-	struct scatterlist * sgpnt;
-	int count, this_count_max;
-	bh = SCpnt->request.bh;
-	this_count = 0;
-	count = 0;
-	this_count_max = (scsi_CDs[dev].ten ? 0xffff : 0xff) << 4;
-	/* Calculate how many links we can use.  First see if we need
-	 * a padding record at the start */
-	this_count = SCpnt->request.sector % 4;
-	if(this_count) count++;
-	while(bh && count < SCpnt->host->sg_tablesize) {
-	    if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
-	    this_count += (bh->b_size >> 9);
-	    count++;
-	    bh = bh->b_reqnext;
-	};
-	/* Fix up in case of an odd record at the end */
-	end_rec = 0;
-	if(this_count % 4) {
-	    if (count < SCpnt->host->sg_tablesize) {
-		count++;
-		end_rec = (4 - (this_count % 4)) << 9;
-		this_count += 4 - (this_count % 4);
-	    } else {
-		count--;
-		this_count -= (this_count % 4);
-	    };
-	};
-	SCpnt->use_sg = count;  /* Number of chains */
-	/* scsi_malloc can only allocate in chunks of 512 bytes */
-	count  = (SCpnt->use_sg * sizeof(struct scatterlist) + 511) & ~511;
-
-	SCpnt->sglist_len = count;
-	sgpnt = (struct scatterlist * ) scsi_malloc(count);
-	if (!sgpnt) {
-	    printk("Warning - running *really* short on DMA buffers\n");
-	    SCpnt->use_sg = 0;  /* No memory left - bail out */
-	} else {
-	    buffer = (unsigned char *) sgpnt;
-	    count = 0;
-	    bh = SCpnt->request.bh;
-	    if(SCpnt->request.sector % 4) {
-		sgpnt[count].length = (SCpnt->request.sector % 4) << 9;
-		sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
-		if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
-		sgpnt[count].alt_address = sgpnt[count].address; /* Flag to delete
-								    if needed */
-		count++;
-	    };
-	    for(bh = SCpnt->request.bh; count < SCpnt->use_sg;
-		count++, bh = bh->b_reqnext) {
-		if (bh) { /* Need a placeholder at the end of the record? */
-		    sgpnt[count].address = bh->b_data;
-		    sgpnt[count].length = bh->b_size;
-		    sgpnt[count].alt_address = NULL;
-		} else {
-		    sgpnt[count].address = (char *) scsi_malloc(end_rec);
-		    if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
-		    sgpnt[count].length = end_rec;
-		    sgpnt[count].alt_address = sgpnt[count].address;
-		    if (count+1 != SCpnt->use_sg) panic("Bad sr request list");
-		    break;
+	     scsi_dma_free_sectors >= 10)) {
+		struct buffer_head *bh;
+		struct scatterlist *sgpnt;
+		int count, this_count_max;
+		bh = SCpnt->request.bh;
+		this_count = 0;
+		count = 0;
+		this_count_max = (scsi_CDs[dev].ten ? 0xffff : 0xff) << 4;
+		/* Calculate how many links we can use.  First see if we need
+		 * a padding record at the start */
+		this_count = SCpnt->request.sector % 4;
+		if (this_count)
+			count++;
+		while (bh && count < SCpnt->host->sg_tablesize) {
+			if ((this_count + (bh->b_size >> 9)) > this_count_max)
+				break;
+			this_count += (bh->b_size >> 9);
+			count++;
+			bh = bh->b_reqnext;
+		};
+		/* Fix up in case of an odd record at the end */
+		end_rec = 0;
+		if (this_count % 4) {
+			if (count < SCpnt->host->sg_tablesize) {
+				count++;
+				end_rec = (4 - (this_count % 4)) << 9;
+				this_count += 4 - (this_count % 4);
+			} else {
+				count--;
+				this_count -= (this_count % 4);
+			};
 		};
-		if (virt_to_phys(sgpnt[count].address) + sgpnt[count].length - 1 >
-		    ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) {
-		    sgpnt[count].alt_address = sgpnt[count].address;
-		    /* We try to avoid exhausting the DMA pool, since it is easier
-		     * to control usage here.  In other places we might have a more
-		     * pressing need, and we would be screwed if we ran out */
-		    if(scsi_dma_free_sectors < (sgpnt[count].length >> 9) + 5) {
-			sgpnt[count].address = NULL;
-		    } else {
-			sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
-		    };
-		    /* If we start running low on DMA buffers, we abort the scatter-gather
-		     * operation, and free all of the memory we have allocated.  We want to
-		     * ensure that all scsi operations are able to do at least a non-scatter/gather
-		     * operation */
-		    if(sgpnt[count].address == NULL){ /* Out of dma memory */
-			printk("Warning: Running low on SCSI DMA buffers\n");
-			/* Try switching back to a non scatter-gather operation. */
-			while(--count >= 0){
-			    if(sgpnt[count].alt_address)
-				scsi_free(sgpnt[count].address, sgpnt[count].length);
+		SCpnt->use_sg = count;	/* Number of chains */
+		/* scsi_malloc can only allocate in chunks of 512 bytes */
+		count = (SCpnt->use_sg * sizeof(struct scatterlist) + 511) & ~511;
+
+		SCpnt->sglist_len = count;
+		sgpnt = (struct scatterlist *) scsi_malloc(count);
+		if (!sgpnt) {
+			printk("Warning - running *really* short on DMA buffers\n");
+			SCpnt->use_sg = 0;	/* No memory left - bail out */
+		} else {
+			buffer = (unsigned char *) sgpnt;
+			count = 0;
+			bh = SCpnt->request.bh;
+			if (SCpnt->request.sector % 4) {
+				sgpnt[count].length = (SCpnt->request.sector % 4) << 9;
+				sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+				if (!sgpnt[count].address)
+					panic("SCSI DMA pool exhausted.");
+				sgpnt[count].alt_address = sgpnt[count].address;	/* Flag to delete
+											   if needed */
+				count++;
 			};
-			SCpnt->use_sg = 0;
-			scsi_free(buffer, SCpnt->sglist_len);
-			break;
-		    }; /* if address == NULL */
-		};  /* if need DMA fixup */
-	    };  /* for loop to fill list */
+			for (bh = SCpnt->request.bh; count < SCpnt->use_sg;
+			     count++, bh = bh->b_reqnext) {
+				if (bh) {	/* Need a placeholder at the end of the record? */
+					sgpnt[count].address = bh->b_data;
+					sgpnt[count].length = bh->b_size;
+					sgpnt[count].alt_address = NULL;
+				} else {
+					sgpnt[count].address = (char *) scsi_malloc(end_rec);
+					if (!sgpnt[count].address)
+						panic("SCSI DMA pool exhausted.");
+					sgpnt[count].length = end_rec;
+					sgpnt[count].alt_address = sgpnt[count].address;
+					if (count + 1 != SCpnt->use_sg)
+						panic("Bad sr request list");
+					break;
+				};
+				if (virt_to_phys(sgpnt[count].address) + sgpnt[count].length - 1 >
+				    ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) {
+					sgpnt[count].alt_address = sgpnt[count].address;
+					/* We try to avoid exhausting the DMA pool, since it is easier
+					 * to control usage here.  In other places we might have a more
+					 * pressing need, and we would be screwed if we ran out */
+					if (scsi_dma_free_sectors < (sgpnt[count].length >> 9) + 5) {
+						sgpnt[count].address = NULL;
+					} else {
+						sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+					};
+					/* If we start running low on DMA buffers, we abort the scatter-gather
+					 * operation, and free all of the memory we have allocated.  We want to
+					 * ensure that all scsi operations are able to do at least a non-scatter/gather
+					 * operation */
+					if (sgpnt[count].address == NULL) {	/* Out of dma memory */
+						printk("Warning: Running low on SCSI DMA buffers\n");
+						/* Try switching back to a non scatter-gather operation. */
+						while (--count >= 0) {
+							if (sgpnt[count].alt_address)
+								scsi_free(sgpnt[count].address, sgpnt[count].length);
+						};
+						SCpnt->use_sg = 0;
+						scsi_free(buffer, SCpnt->sglist_len);
+						break;
+					};	/* if address == NULL */
+				};	/* if need DMA fixup */
+			};	/* for loop to fill list */
 #ifdef DEBUG
-	    printk("SR: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
-		   this_count,
-		   SCpnt->request.current_nr_sectors,
-		   SCpnt->request.nr_sectors);
-	    for(count=0; count<SCpnt->use_sg; count++)
-		printk("SGlist: %d %x %x %x\n", count,
-		       sgpnt[count].address,
-		       sgpnt[count].alt_address,
-		       sgpnt[count].length);
+			printk("SR: %d %d %d %d %d *** ", SCpnt->use_sg, SCpnt->request.sector,
+			       this_count,
+			       SCpnt->request.current_nr_sectors,
+			       SCpnt->request.nr_sectors);
+			for (count = 0; count < SCpnt->use_sg; count++)
+				printk("SGlist: %d %x %x %x\n", count,
+				       sgpnt[count].address,
+				       sgpnt[count].alt_address,
+				       sgpnt[count].length);
 #endif
-	};  /* Able to allocate scatter-gather list */
+		};		/* Able to allocate scatter-gather list */
 	};
 
-	if (SCpnt->use_sg == 0){
-	/* We cannot use scatter-gather.  Do this the old fashion way */
-	if (!SCpnt->request.bh)
-	    this_count = SCpnt->request.nr_sectors;
-	else
-	    this_count = (SCpnt->request.bh->b_size >> 9);
+	if (SCpnt->use_sg == 0) {
+		/* We cannot use scatter-gather.  Do this the old fashion way */
+		if (!SCpnt->request.bh)
+			this_count = SCpnt->request.nr_sectors;
+		else
+			this_count = (SCpnt->request.bh->b_size >> 9);
 
-	start = block % 4;
-	if (start)
-	    {
-	    this_count = ((this_count > 4 - start) ?
-			  (4 - start) : (this_count));
-	    buffer = (unsigned char *) scsi_malloc(2048);
-	    }
-	else if (this_count < 4)
-	    {
-	    buffer = (unsigned char *) scsi_malloc(2048);
-	    }
-	else
-	    {
-	    this_count -= this_count % 4;
-	    buffer = (unsigned char *) SCpnt->request.buffer;
-	    if (virt_to_phys(buffer) + (this_count << 9) > ISA_DMA_THRESHOLD &&
-		SCpnt->host->unchecked_isa_dma)
-		buffer = (unsigned char *) scsi_malloc(this_count << 9);
-	    }
+		start = block % 4;
+		if (start) {
+			this_count = ((this_count > 4 - start) ?
+				      (4 - start) : (this_count));
+			buffer = (unsigned char *) scsi_malloc(2048);
+		} else if (this_count < 4) {
+			buffer = (unsigned char *) scsi_malloc(2048);
+		} else {
+			this_count -= this_count % 4;
+			buffer = (unsigned char *) SCpnt->request.buffer;
+			if (virt_to_phys(buffer) + (this_count << 9) > ISA_DMA_THRESHOLD &&
+			    SCpnt->host->unchecked_isa_dma)
+				buffer = (unsigned char *) scsi_malloc(this_count << 9);
+		}
 	};
 
 	if (scsi_CDs[dev].sector_size == 2048)
-	block = block >> 2; /* These are the sectors that the cdrom uses */
+		block = block >> 2;	/* These are the sectors that the cdrom uses */
 	else
-	block = block & 0xfffffffc;
+		block = block & 0xfffffffc;
 
 	realcount = (this_count + 3) / 4;
 
-	if (scsi_CDs[dev].sector_size == 512) realcount = realcount << 2;
+	if (scsi_CDs[dev].sector_size == 512)
+		realcount = realcount << 2;
 
-        /*
-         * Note: The scsi standard says that READ_6 is *optional*, while
-         * READ_10 is mandatory.   Thus there is no point in using
-         * READ_6.
-         */
-	if (scsi_CDs[dev].ten)
-          
-    {
-		if (realcount > 0xffff)
-	{
+	/*
+	 * Note: The scsi standard says that READ_6 is *optional*, while
+	 * READ_10 is mandatory.   Thus there is no point in using
+	 * READ_6.
+	 */
+	if (scsi_CDs[dev].ten) {
+		if (realcount > 0xffff) {
 			realcount = 0xffff;
 			this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
-	}
-
-		cmd[0] += READ_10 - READ_6 ;
+		}
+		cmd[0] += READ_10 - READ_6;
 		cmd[2] = (unsigned char) (block >> 24) & 0xff;
 		cmd[3] = (unsigned char) (block >> 16) & 0xff;
 		cmd[4] = (unsigned char) (block >> 8) & 0xff;
@@ -777,164 +756,172 @@
 		cmd[6] = cmd[9] = 0;
 		cmd[7] = (unsigned char) (realcount >> 8) & 0xff;
 		cmd[8] = (unsigned char) realcount & 0xff;
-    }
-	else
-    {
-	if (realcount > 0xff)
-	{
-	    realcount = 0xff;
-	    this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+	} else {
+		if (realcount > 0xff) {
+			realcount = 0xff;
+			this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+		}
+		cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+		cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+		cmd[3] = (unsigned char) block & 0xff;
+		cmd[4] = (unsigned char) realcount;
+		cmd[5] = 0;
 	}
 
-	cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
-	cmd[2] = (unsigned char) ((block >> 8) & 0xff);
-	cmd[3] = (unsigned char) block & 0xff;
-	cmd[4] = (unsigned char) realcount;
-	cmd[5] = 0;
-    }
-
 #ifdef DEBUG
-    {
-	int i;
-	printk("ReadCD: %d %d %d %d\n",block, realcount, buffer, this_count);
-	printk("Use sg: %d\n", SCpnt->use_sg);
-	printk("Dumping command: ");
-	for(i=0; i<12; i++) printk("%2.2x ", cmd[i]);
-	printk("\n");
-    };
+	{
+		int i;
+		printk("ReadCD: %d %d %d %d\n", block, realcount, buffer, this_count);
+		printk("Use sg: %d\n", SCpnt->use_sg);
+		printk("Dumping command: ");
+		for (i = 0; i < 12; i++)
+			printk("%2.2x ", cmd[i]);
+		printk("\n");
+	};
 #endif
 
-    /* Some dumb host adapters can speed transfers by knowing the
-     * minimum transfersize in advance.
-     *
-     * We shouldn't disconnect in the middle of a sector, but the cdrom
-     * sector size can be larger than the size of a buffer and the
-     * transfer may be split to the size of a buffer.  So it's safe to
-     * assume that we can at least transfer the minimum of the buffer
-     * size (1024) and the sector size between each connect / disconnect.
-     */
+	/* Some dumb host adapters can speed transfers by knowing the
+	 * minimum transfersize in advance.
+	 *
+	 * We shouldn't disconnect in the middle of a sector, but the cdrom
+	 * sector size can be larger than the size of a buffer and the
+	 * transfer may be split to the size of a buffer.  So it's safe to
+	 * assume that we can at least transfer the minimum of the buffer
+	 * size (1024) and the sector size between each connect / disconnect.
+	 */
 
-    SCpnt->transfersize = (scsi_CDs[dev].sector_size > 1024) ?
-	1024 : scsi_CDs[dev].sector_size;
+	SCpnt->transfersize = (scsi_CDs[dev].sector_size > 1024) ?
+	    1024 : scsi_CDs[dev].sector_size;
 
 	SCpnt->this_count = this_count;
-	scsi_do_cmd (SCpnt, (void *) cmd, buffer,
-		 realcount * scsi_CDs[dev].sector_size,
-		 rw_intr, SR_TIMEOUT, MAX_RETRIES);
+	scsi_do_cmd(SCpnt, (void *) cmd, buffer,
+		    realcount * scsi_CDs[dev].sector_size,
+		    rw_intr, SR_TIMEOUT, MAX_RETRIES);
 }
 
-static int sr_detect(Scsi_Device * SDp){
+static int sr_detect(Scsi_Device * SDp)
+{
 
-    if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 0;
+	if (SDp->type != TYPE_ROM && SDp->type != TYPE_WORM)
+		return 0;
 
-    printk("Detected scsi CD-ROM sr%d at scsi%d, channel %d, id %d, lun %d\n",
-	   sr_template.dev_noticed++,
-	   SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+	printk("Detected scsi CD-ROM sr%d at scsi%d, channel %d, id %d, lun %d\n",
+	       sr_template.dev_noticed++,
+	       SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
 
-    return 1;
+	return 1;
 }
 
-static int sr_attach(Scsi_Device * SDp){
-    Scsi_CD * cpnt;
-    int i;
-
-    if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 1;
-
-    if (sr_template.nr_dev >= sr_template.dev_max)
-    {
-	SDp->attached--;
-	return 1;
-    }
+static int sr_attach(Scsi_Device * SDp)
+{
+	Scsi_CD *cpnt;
+	int i;
+
+	if (SDp->type != TYPE_ROM && SDp->type != TYPE_WORM)
+		return 1;
 
-    for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
-	if(!cpnt->device) break;
+	if (sr_template.nr_dev >= sr_template.dev_max) {
+		SDp->attached--;
+		return 1;
+	}
+	for (cpnt = scsi_CDs, i = 0; i < sr_template.dev_max; i++, cpnt++)
+		if (!cpnt->device)
+			break;
 
-    if(i >= sr_template.dev_max) panic ("scsi_devices corrupt (sr)");
+	if (i >= sr_template.dev_max)
+		panic("scsi_devices corrupt (sr)");
 
-    SDp->scsi_request_fn = do_sr_request;
-    scsi_CDs[i].device = SDp;
+	SDp->scsi_request_fn = do_sr_request;
+	scsi_CDs[i].device = SDp;
 
-    sr_template.nr_dev++;
-    if(sr_template.nr_dev > sr_template.dev_max)
-	panic ("scsi_devices corrupt (sr)");
-    return 0;
+	sr_template.nr_dev++;
+	if (sr_template.nr_dev > sr_template.dev_max)
+		panic("scsi_devices corrupt (sr)");
+	return 0;
 }
 
 
-static void sr_init_done (Scsi_Cmnd * SCpnt)
+static void sr_init_done(Scsi_Cmnd * SCpnt)
 {
-    struct request * req;
+	struct request *req;
 
-    req = &SCpnt->request;
-    req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+	req = &SCpnt->request;
+	req->rq_status = RQ_SCSI_DONE;	/* Busy, but indicate request done */
 
-    if (req->sem != NULL) {
-	up(req->sem);
-    }
+	if (req->sem != NULL) {
+		up(req->sem);
+	}
 }
 
-void get_sectorsize(int i){
-    unsigned char cmd[10];
-    unsigned char *buffer;
-    int the_result, retries;
-    Scsi_Cmnd * SCpnt;
-    unsigned long flags;
-
-    buffer = (unsigned char *) scsi_malloc(512);
-    SCpnt = scsi_allocate_device(NULL, scsi_CDs[i].device, 1);
-
-    retries = 3;
-    do {
-	cmd[0] = READ_CAPACITY;
-	cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
-	memset ((void *) &cmd[2], 0, 8);
-	SCpnt->request.rq_status = RQ_SCSI_BUSY;  /* Mark as really busy */
-	SCpnt->cmd_len = 0;
+void get_sectorsize(int i)
+{
+	unsigned char cmd[10];
+	unsigned char *buffer;
+	int the_result, retries;
+	Scsi_Cmnd *SCpnt;
+	unsigned long flags;
+
+	buffer = (unsigned char *) scsi_malloc(512);
+	SCpnt = scsi_allocate_device(NULL, scsi_CDs[i].device, 1);
+
+	retries = 3;
+	do {
+		cmd[0] = READ_CAPACITY;
+		cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+		memset((void *) &cmd[2], 0, 8);
+		SCpnt->request.rq_status = RQ_SCSI_BUSY;	/* Mark as really busy */
+		SCpnt->cmd_len = 0;
+
+		memset(buffer, 0, 8);
+
+		/* Do the command and wait.. */
+		{
+			struct semaphore sem = MUTEX_LOCKED;
+			SCpnt->request.sem = &sem;
+			spin_lock_irqsave(&io_request_lock, flags);
+			scsi_do_cmd(SCpnt,
+				    (void *) cmd, (void *) buffer,
+				    512, sr_init_done, SR_TIMEOUT,
+				    MAX_RETRIES);
+			spin_unlock_irqrestore(&io_request_lock, flags);
+			down(&sem);
+		}
 
-	memset(buffer, 0, 8);
+		the_result = SCpnt->result;
+		retries--;
 
-	/* Do the command and wait.. */
-	{
-	    struct semaphore sem = MUTEX_LOCKED;
-	    SCpnt->request.sem = &sem;
-	    spin_lock_irqsave(&io_request_lock, flags);
-	    scsi_do_cmd (SCpnt,
-			 (void *) cmd, (void *) buffer,
-			 512, sr_init_done,  SR_TIMEOUT,
-			 MAX_RETRIES);
-	    spin_unlock_irqrestore(&io_request_lock, flags);
-	    down(&sem);
-	}
-
-	the_result = SCpnt->result;
-	retries--;
-
-    } while(the_result && retries);
-
-
-    wake_up(&SCpnt->device->device_wait);
-    scsi_release_command(SCpnt);
-    SCpnt = NULL;
-
-    if (the_result) {
-	scsi_CDs[i].capacity = 0x1fffff;
-	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]);
-	scsi_CDs[i].sector_size = (buffer[4] << 24) |
-	    (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
-	switch (scsi_CDs[i].sector_size) {
-		/*
-		 * HP 4020i CD-Recorder reports 2340 byte sectors
-		 * Philips CD-Writers report 2352 byte sectors
-		 *
-		 * Use 2k sectors for them..
-		 */
-		case 0: case 2340: case 2352:
+	} while (the_result && retries);
+
+
+	wake_up(&SCpnt->device->device_wait);
+	scsi_release_command(SCpnt);
+	SCpnt = NULL;
+
+	if (the_result) {
+		scsi_CDs[i].capacity = 0x1fffff;
+		scsi_CDs[i].sector_size = 2048;		/* A guess, just in case */
+		scsi_CDs[i].needs_sector_size = 1;
+	} else {
+#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) {
+			/*
+			 * HP 4020i CD-Recorder reports 2340 byte sectors
+			 * Philips CD-Writers report 2352 byte sectors
+			 *
+			 * Use 2k sectors for them..
+			 */
+		case 0:
+		case 2340:
+		case 2352:
 			scsi_CDs[i].sector_size = 2048;
 			/* fall through */
 		case 2048:
@@ -943,229 +930,307 @@
 		case 512:
 			break;
 		default:
-			printk ("sr%d: unsupported sector size %d.\n",
-				i, scsi_CDs[i].sector_size);
+			printk("sr%d: unsupported sector size %d.\n",
+			       i, scsi_CDs[i].sector_size);
 			scsi_CDs[i].capacity = 0;
 			scsi_CDs[i].needs_sector_size = 1;
-	}
+		}
 
-        /*
-         * Add this so that we have the ability to correctly gauge
-         * what the device is capable of.
-         */
-	scsi_CDs[i].needs_sector_size = 0;
-	sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
-    };
-    scsi_free(buffer, 512);
+		/*
+		 * Add this so that we have the ability to correctly gauge
+		 * what the device is capable of.
+		 */
+		scsi_CDs[i].needs_sector_size = 0;
+		sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+	};
+	scsi_free(buffer, 512);
 }
 
-void get_capabilities(int i){
-    unsigned char cmd[6];
-    unsigned char *buffer;
-    int           rc,n;
-
-    static char *loadmech[] = {
-        "caddy",
-        "tray",
-        "pop-up",
-        "",
-        "changer",
-        "changer",
-        "",
-        ""
-    };          
-
-    buffer = (unsigned char *) scsi_malloc(512);
-    cmd[0] = MODE_SENSE;
-    cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
-    cmd[2] = 0x2a;
-    cmd[4] = 128;
-    cmd[3] = cmd[5] = 0;
-    rc = sr_do_ioctl(i, cmd, buffer, 128, 1);
-    
-    if (-EINVAL == rc) {
-        /* failed, drive has'nt this mode page */
-        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)
+void get_capabilities(int i)
+{
+	unsigned char cmd[6];
+	unsigned char *buffer;
+	int rc, n;
+
+	static char *loadmech[] =
+	{
+		"caddy",
+		"tray",
+		"pop-up",
+		"",
+		"changer",
+		"cartridge changer",
+		"",
+		""
+	};
+
+	buffer = (unsigned char *) scsi_malloc(512);
+	cmd[0] = MODE_SENSE;
+	cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+	cmd[2] = 0x2a;
+	cmd[4] = 128;
+	cmd[3] = cmd[5] = 0;
+	rc = sr_do_ioctl(i, cmd, buffer, 128, 1);
+
+	if (-EINVAL == rc) {
+		/* failed, drive has'nt this mode page */
+		scsi_CDs[i].cdi.speed = 1;
+		/* disable speed select, drive probably can't do this either */
+		scsi_CDs[i].cdi.mask |= CDC_SELECT_SPEED;
+		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;
-    }
-    scsi_free(buffer, 512);
+		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;
+	if ((buffer[n+6] & 0x8) == 0)
+		/* can't eject */
+		scsi_CDs[i].cdi.mask |= CDC_OPEN_TRAY;
+
+	if ((buffer[n+6] >> 5) == mechtype_individual_changer ||
+	    (buffer[n+6] >> 5) == mechtype_cartridge_changer)
+		scsi_CDs[i].cdi.capacity = 
+			cdrom_number_of_slots(&(scsi_CDs[i].cdi));
+	if (scsi_CDs[i].cdi.capacity <= 1)
+                /* not a changer */
+		scsi_CDs[i].cdi.mask |= CDC_SELECT_DISC;
+	/*else    I don't think it can close its tray
+		scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY; */
+
+
+	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;
 
 static int sr_init()
 {
-    int i;
+	int i;
 
-    if(sr_template.dev_noticed == 0) return 0;
+	if (sr_template.dev_noticed == 0)
+		return 0;
 
-    if(!sr_registered) {
-	if (register_blkdev(MAJOR_NR,"sr",&cdrom_fops)) {
-	    printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR);
-	    return 1;
+	if (!sr_registered) {
+		if (register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) {
+			printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR);
+			return 1;
+		}
+		sr_registered++;
 	}
-	sr_registered++;
-    }
+	if (scsi_CDs)
+		return 0;
+	sr_template.dev_max =
+	    sr_template.dev_noticed + SR_EXTRA_DEVS;
+	scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
+	memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
 
+	sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+	memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
 
-    if (scsi_CDs) return 0;
-    sr_template.dev_max =
-            sr_template.dev_noticed + SR_EXTRA_DEVS;
-    scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
-    memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
+	sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
+						 sizeof(int), GFP_ATOMIC);
 
-    sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
-    memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
-
-    sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
-					 sizeof(int), GFP_ATOMIC);
-
-    /*
-     * These are good guesses for the time being.
-     */
-    for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
-    blksize_size[MAJOR_NR] = sr_blocksizes;
-    return 0;
+	/*
+	 * These are good guesses for the time being.
+	 */
+	for (i = 0; i < sr_template.dev_max; i++)
+		sr_blocksizes[i] = 2048;
+	blksize_size[MAJOR_NR] = sr_blocksizes;
+	return 0;
 }
 
 void sr_finish()
 {
-    int i;
-    char name[6];
+	int i;
+	char name[6];
 
-    blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-    blk_size[MAJOR_NR] = sr_sizes;
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	blk_size[MAJOR_NR] = sr_sizes;
 
-    for (i = 0; i < sr_template.nr_dev; ++i)
-    {
-	/* If we have already seen this, then skip it.  Comes up
-	 * with loadable modules. */
-	if (scsi_CDs[i].capacity) continue;
-	scsi_CDs[i].capacity = 0x1fffff;
-	scsi_CDs[i].sector_size = 2048;  /* A guess, just in case */
-	scsi_CDs[i].needs_sector_size = 1;
-	scsi_CDs[i].device->changed = 1; /* force recheck CD type */
+	for (i = 0; i < sr_template.nr_dev; ++i) {
+		/* If we have already seen this, then skip it.  Comes up
+		 * with loadable modules. */
+		if (scsi_CDs[i].capacity)
+			continue;
+		scsi_CDs[i].capacity = 0x1fffff;
+		scsi_CDs[i].sector_size = 2048;		/* A guess, just in case */
+		scsi_CDs[i].needs_sector_size = 1;
+		scsi_CDs[i].device->changed = 1;	/* force recheck CD type */
 #if 0
-	/* seems better to leave this for later */
-	get_sectorsize(i);
-	printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
+		/* seems better to leave this for later */
+		get_sectorsize(i);
+		printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
 #endif
-	scsi_CDs[i].use = 1;
-	scsi_CDs[i].ten = 1;
-	scsi_CDs[i].remap = 1;
-      	scsi_CDs[i].readcd_known = 0;
-      	scsi_CDs[i].readcd_cdda  = 0;
-	sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
-
-	scsi_CDs[i].cdi.ops        = &sr_dops;
-	scsi_CDs[i].cdi.handle     = &scsi_CDs[i];
-	scsi_CDs[i].cdi.dev        = MKDEV(MAJOR_NR,i);
-	scsi_CDs[i].cdi.mask       = 0;
-        scsi_CDs[i].cdi.capacity   = 1;
-	get_capabilities(i);
-	sr_vendor_init(i);
-
-	sprintf(name, "sr%d", i);
-	strcpy(scsi_CDs[i].cdi.name, name);
-	register_cdrom(&scsi_CDs[i].cdi);
-    }
-
-
-    /* If our host adapter is capable of scatter-gather, then we increase
-     * the read-ahead to 16 blocks (32 sectors).  If not, we use
-     * a two block (4 sector) read ahead. */
-    if(scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize)
-	read_ahead[MAJOR_NR] = 32;  /* 32 sector read-ahead.  Always removable. */
-    else
-	read_ahead[MAJOR_NR] = 4;  /* 4 sector read-ahead */
+		scsi_CDs[i].use = 1;
+		scsi_CDs[i].ten = 1;
+		scsi_CDs[i].remap = 1;
+		scsi_CDs[i].readcd_known = 0;
+		scsi_CDs[i].readcd_cdda = 0;
+		sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+
+		scsi_CDs[i].cdi.ops = &sr_dops;
+		scsi_CDs[i].cdi.handle = &scsi_CDs[i];
+		scsi_CDs[i].cdi.dev = MKDEV(MAJOR_NR, i);
+		scsi_CDs[i].cdi.mask = 0;
+		scsi_CDs[i].cdi.capacity = 1;
+		get_capabilities(i);
+		sr_vendor_init(i);
+
+		sprintf(name, "sr%d", i);
+		strcpy(scsi_CDs[i].cdi.name, name);
+		register_cdrom(&scsi_CDs[i].cdi);
+	}
 
-    return;
+
+	/* If our host adapter is capable of scatter-gather, then we increase
+	 * the read-ahead to 16 blocks (32 sectors).  If not, we use
+	 * a two block (4 sector) read ahead. */
+	if (scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize)
+		read_ahead[MAJOR_NR] = 32;	/* 32 sector read-ahead.  Always removable. */
+	else
+		read_ahead[MAJOR_NR] = 4;	/* 4 sector read-ahead */
+
+	return;
 }
 
 static void sr_detach(Scsi_Device * SDp)
 {
-    Scsi_CD * cpnt;
-    int i;
+	Scsi_CD *cpnt;
+	int i;
 
-    for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
-	if(cpnt->device == SDp) {
-	    kdev_t devi = MKDEV(MAJOR_NR, i);
-	    struct super_block * sb = get_super(devi);
-
-	    /*
-	     * Since the cdrom is read-only, no need to sync the device.
-	     * We should be kind to our buffer cache, however.
-	     */
-	    if (sb) invalidate_inodes(sb);
-	    invalidate_buffers(devi);
-
-	    /*
-	     * Reset things back to a sane state so that one can re-load a new
-	     * driver (perhaps the same one).
-	     */
-            unregister_cdrom(&(cpnt->cdi));
-	    cpnt->device = NULL;
-	    cpnt->capacity = 0;
-	    SDp->attached--;
-	    sr_template.nr_dev--;
-	    sr_template.dev_noticed--;
-	    sr_sizes[i] = 0;
-	    return;
-	}
-    return;
+	for (cpnt = scsi_CDs, i = 0; i < sr_template.dev_max; i++, cpnt++)
+		if (cpnt->device == SDp) {
+			kdev_t devi = MKDEV(MAJOR_NR, i);
+			struct super_block *sb = get_super(devi);
+
+			/*
+			 * Since the cdrom is read-only, no need to sync the device.
+			 * We should be kind to our buffer cache, however.
+			 */
+			if (sb)
+				invalidate_inodes(sb);
+			invalidate_buffers(devi);
+
+			/*
+			 * Reset things back to a sane state so that one can re-load a new
+			 * driver (perhaps the same one).
+			 */
+			unregister_cdrom(&(cpnt->cdi));
+			cpnt->device = NULL;
+			cpnt->capacity = 0;
+			SDp->attached--;
+			sr_template.nr_dev--;
+			sr_template.dev_noticed--;
+			sr_sizes[i] = 0;
+			return;
+		}
+	return;
 }
 
 
 #ifdef MODULE
 
-int init_module(void) {
-        sr_template.module = &__this_module;
-        return scsi_register_module(MODULE_SCSI_DEV, &sr_template);
+int init_module(void)
+{
+	sr_template.module = &__this_module;
+	return scsi_register_module(MODULE_SCSI_DEV, &sr_template);
 }
 
-void cleanup_module( void)
+void cleanup_module(void)
 {
-    scsi_unregister_module(MODULE_SCSI_DEV, &sr_template);
-    unregister_blkdev(MAJOR_NR, "sr");
-    sr_registered--;
-    if(scsi_CDs != NULL) {
-	scsi_init_free((char *) scsi_CDs,
-		       (sr_template.dev_noticed + SR_EXTRA_DEVS)
-		       * sizeof(Scsi_CD));
-
-	scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
-        sr_sizes = NULL;
-
-	scsi_init_free((char *) sr_blocksizes, sr_template.dev_max * sizeof(int));
-        sr_blocksizes = NULL;
-    }
-
-    blksize_size[MAJOR_NR] = NULL;
-    blk_dev[MAJOR_NR].request_fn = NULL;
-    blk_size[MAJOR_NR] = NULL;
-    read_ahead[MAJOR_NR] = 0;
+	scsi_unregister_module(MODULE_SCSI_DEV, &sr_template);
+	unregister_blkdev(MAJOR_NR, "sr");
+	sr_registered--;
+	if (scsi_CDs != NULL) {
+		scsi_init_free((char *) scsi_CDs,
+			       (sr_template.dev_noticed + SR_EXTRA_DEVS)
+			       * sizeof(Scsi_CD));
+
+		scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
+		sr_sizes = NULL;
 
-    sr_template.dev_max = 0;
+		scsi_init_free((char *) sr_blocksizes, sr_template.dev_max * sizeof(int));
+		sr_blocksizes = NULL;
+	}
+	blksize_size[MAJOR_NR] = NULL;
+	blk_dev[MAJOR_NR].request_fn = NULL;
+	blk_size[MAJOR_NR] = NULL;
+	read_ahead[MAJOR_NR] = 0;
+
+	sr_template.dev_max = 0;
 }
-#endif /* MODULE */
+
+#endif				/* MODULE */
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
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	Thu Sep  9 12:27:30 1999
+++ linux/drivers/scsi/sr_ioctl.c	Sun Sep 12 16:17: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,105 +286,22 @@
 /* ----------------------------------------------------------------------- */
 /* 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)
 {
     u_char  sr_cmd[10];    
-    int result, target;
-    
-    target = MINOR(cdi->dev);
+    int result, target  = MINOR(cdi->dev);
+    unsigned char buffer[32];
     
     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;
 	
-	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 +309,19 @@
 	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;
 	
-	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 +330,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 +343,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 +383,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 +429,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 +484,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/fs/isofs/inode.c linux/fs/isofs/inode.c
--- /tmp/linux-2.2.12/fs/isofs/inode.c	Tue Dec 29 20:40:35 1998
+++ linux/fs/isofs/inode.c	Sun Sep 12 16:31:11 1999
@@ -411,7 +411,7 @@
   struct inode inode_fake;
   struct file_operations *fops;
   extern struct file_operations * get_blkfops(unsigned int);
-  int i;
+  int i, session = 0;
 
   vol_desc_start=0;
   fops = get_blkfops(MAJOR(dev));
@@ -424,6 +424,27 @@
       mm_segment_t old_fs=get_fs();
       inode_fake.i_rdev=dev;
       ms_info.addr_format=CDROM_LBA;
+      /* If a minor device was explicitly opened, set session to the
+       * minor number. For instance, if /dev/hdc1 is mounted, session
+       * 1 on the CD-ROM is selected. CD_PART_MAX gives access to
+       * a max of 64 sessions on IDE. SCSI drives must still use
+       * the session option to mount.
+       */
+      if ((MINOR(dev) % CD_PART_MAX) && (MAJOR(dev) != SCSI_CDROM_MAJOR))
+		session = MINOR(dev) % CD_PART_MAX;
+      if (session > 0 && session <= CD_PART_MAX) {
+		struct cdrom_tocentry entry;
+		entry.cdte_track=session;
+		entry.cdte_format=CDROM_LBA;
+		set_fs(KERNEL_DS);
+		i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
+			      NULL, CDROMREADTOCENTRY, (unsigned long) &entry);
+		set_fs(old_fs);
+		if(!i) printk(KERN_ERR"Session %d start %d type %d\n",session,entry.cdte_addr.lba,entry.cdte_ctrl&CDROM_DATA_TRACK);
+		if(i || (entry.cdte_ctrl & CDROM_DATA_TRACK) != 4)
+			printk(KERN_ERR"Invalid session number or type of track\n");
+		else return entry.cdte_addr.lba;
+      }
       set_fs(KERNEL_DS);
       i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
 				       NULL,
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 Mar 22 03:37:56 1999
+++ linux/include/linux/cdrom.h	Tue Sep 14 21:44:13 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
  *******************************************************/
@@ -126,9 +147,9 @@
 /* Address in MSF format */
 struct cdrom_msf0		
 {
-	u_char	minute;
-	u_char	second;
-	u_char	frame;
+	__u8	minute;
+	__u8	second;
+	__u8	frame;
 };
 
 /* Address in either MSF or logical format */
@@ -141,48 +162,48 @@
 /* This struct is used by the CDROMPLAYMSF ioctl */ 
 struct cdrom_msf 
 {
-	u_char	cdmsf_min0;	/* start minute */
-	u_char	cdmsf_sec0;	/* start second */
-	u_char	cdmsf_frame0;	/* start frame */
-	u_char	cdmsf_min1;	/* end minute */
-	u_char	cdmsf_sec1;	/* end second */
-	u_char	cdmsf_frame1;	/* end frame */
+	__u8	cdmsf_min0;	/* start minute */
+	__u8	cdmsf_sec0;	/* start second */
+	__u8	cdmsf_frame0;	/* start frame */
+	__u8	cdmsf_min1;	/* end minute */
+	__u8	cdmsf_sec1;	/* end second */
+	__u8	cdmsf_frame1;	/* end frame */
 };
 
 /* This struct is used by the CDROMPLAYTRKIND ioctl */
 struct cdrom_ti 
 {
-	u_char	cdti_trk0;	/* start track */
-	u_char	cdti_ind0;	/* start index */
-	u_char	cdti_trk1;	/* end track */
-	u_char	cdti_ind1;	/* end index */
+	__u8	cdti_trk0;	/* start track */
+	__u8	cdti_ind0;	/* start index */
+	__u8	cdti_trk1;	/* end track */
+	__u8	cdti_ind1;	/* end index */
 };
 
 /* This struct is used by the CDROMREADTOCHDR ioctl */
 struct cdrom_tochdr 	
 {
-	u_char	cdth_trk0;	/* start track */
-	u_char	cdth_trk1;	/* end track */
+	__u8	cdth_trk0;	/* start track */
+	__u8	cdth_trk1;	/* end track */
 };
 
 /* This struct is used by the CDROMVOLCTRL and CDROMVOLREAD ioctls */
 struct cdrom_volctrl
 {
-	u_char	channel0;
-	u_char	channel1;
-	u_char	channel2;
-	u_char	channel3;
+	__u8	channel0;
+	__u8	channel1;
+	__u8	channel2;
+	__u8	channel3;
 };
 
 /* This struct is used by the CDROMSUBCHNL ioctl */
 struct cdrom_subchnl 
 {
-	u_char	cdsc_format;
-	u_char	cdsc_audiostatus;
-	u_char	cdsc_adr:	4;
-	u_char	cdsc_ctrl:	4;
-	u_char	cdsc_trk;
-	u_char	cdsc_ind;
+	__u8	cdsc_format;
+	__u8	cdsc_audiostatus;
+	__u8	cdsc_adr:	4;
+	__u8	cdsc_ctrl:	4;
+	__u8	cdsc_trk;
+	__u8	cdsc_ind;
 	union cdrom_addr cdsc_absaddr;
 	union cdrom_addr cdsc_reladdr;
 };
@@ -191,12 +212,12 @@
 /* This struct is used by the CDROMREADTOCENTRY ioctl */
 struct cdrom_tocentry 
 {
-	u_char	cdte_track;
-	u_char	cdte_adr	:4;
-	u_char	cdte_ctrl	:4;
-	u_char	cdte_format;
+	__u8	cdte_track;
+	__u8	cdte_adr	:4;
+	__u8	cdte_ctrl	:4;
+	__u8	cdte_format;
 	union cdrom_addr cdte_addr;
-	u_char	cdte_datamode;
+	__u8	cdte_datamode;
 };
 
 /* This struct is used by the CDROMREADMODE1, and CDROMREADMODE2 ioctls */
@@ -211,9 +232,9 @@
 struct cdrom_read_audio
 {
 	union cdrom_addr addr; /* frame address */
-	u_char addr_format;    /* CDROM_LBA or CDROM_MSF */
+	__u8 addr_format;    /* CDROM_LBA or CDROM_MSF */
 	int nframes;           /* number of 2352-byte-frames to read at once */
-	u_char *buf;           /* frame buffer (size: nframes*2352 bytes) */
+	__u8 *buf;           /* frame buffer (size: nframes*2352 bytes) */
 };
 
 /* This struct is used with the CDROMMULTISESSION ioctl */
@@ -222,8 +243,8 @@
 	union cdrom_addr addr; /* frame address: start-of-last-session 
 	                           (not the new "frame 16"!).  Only valid
 	                           if the "xa_flag" is true. */
-	u_char xa_flag;        /* 1: "is XA disk" */
-	u_char addr_format;    /* CDROM_LBA or CDROM_MSF */
+	__u8 xa_flag;        /* 1: "is XA disk" */
+	__u8 addr_format;    /* CDROM_LBA or CDROM_MSF */
 };
 
 /* This struct is used with the CDROM_GET_MCN ioctl.  
@@ -233,7 +254,7 @@
  */  
 struct cdrom_mcn 
 {
-  u_char medium_catalog_number[14]; /* 13 ASCII digits, null-terminated */
+  __u8 medium_catalog_number[14]; /* 13 ASCII digits, null-terminated */
 };
 
 /* This is used by the CDROMPLAYBLK ioctl */
@@ -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,260 @@
 #define CDSL_NONE       	((int) (~0U>>1)-1)
 #define CDSL_CURRENT    	((int) (~0U>>1))
 
+/* For partition based multisession access. IDE can handle 64 partitions
+ * per drive - SCSI CD-ROM's use minors to differentiate between the
+ * various drives, so we can't do multisessions the same way there.
+ * Use the -o session=x option to mount on them.
+ */
+#define CD_PART_MAX		64
+#define CD_PART_MASK		(CD_PART_MAX - 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 {
+	__u8 book_version	: 4;
+	__u8 book_type	: 4;
+	__u8 min_rate		: 4;
+	__u8 disc_size	: 4;
+	__u8 layer_type	: 4;
+	__u8 track_path	: 1;
+	__u8 nlayers		: 2;
+	__u8 track_density	: 4;
+	__u8 linear_density	: 4;
+	__u8 bca		: 1;
+	__u8 start_sector;
+	__u8 end_sector;
+	__u8 end_sector_l0;
+};
+
+struct dvd_physical {
+	__u8 type;
+	__u8 layer_num;
+	struct dvd_layer layer[4];
+};
+
+struct dvd_copyright {
+	__u8 type;
+
+	__u8 layer_num;
+	__u8 cpst;
+	__u8 rmi;
+};
+
+struct dvd_disckey {
+	__u8 type;
+
+	unsigned agid			: 2;
+	__u8 value[2048];
+};
+
+struct dvd_bca {
+	__u8 type;
+
+	int len;
+	__u8 value[188];
+};
+
+struct dvd_manufact {
+	__u8 type;
+
+	__u8 layer_num;
+	int len;
+	__u8 value[2048];
+};
+
+typedef union {
+	__u8 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 __u8 dvd_key[5];		/* 40-bit value, MSB is first elem. */
+typedef __u8 dvd_challenge[10];	/* 80-bit value, MSB is first elem. */
+
+struct dvd_lu_send_agid {
+	__u8 type;
+	unsigned agid		: 2;
+};
+
+struct dvd_host_send_challenge {
+	__u8 type;
+	unsigned agid		: 2;
+
+	dvd_challenge chal;
+};
+
+struct dvd_send_key {
+	__u8 type;
+	unsigned agid		: 2;
+
+	dvd_key key;
+};
+
+struct dvd_lu_send_challenge {
+	__u8 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 {
+	__u8 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 {
+	__u8 type;
+	unsigned agid		: 2;
+
+	unsigned asf		: 1;
+};
+
+typedef union {
+	__u8 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 {
@@ -382,11 +666,13 @@
 	int speed;			/* maximum speed for reading data */
 	int capacity;			/* number of discs in jukebox */
 /* device-related storage */
-	int options : 30;               /* options flags */
-	unsigned mc_flags : 2;          /* media change buffer flags */
+	int options		: 30;	/* options flags */
+	unsigned mc_flags	: 2;	/* media change buffer flags */
     	int use_count;                  /* number of times device opened */
     	char name[20];                  /* name of the device type */
-
+/* per-device flags */
+        __u8 sanyo_slot		: 2;	/* Sanyo 3 CD changer support */
+        __u8 reserved		: 6;	/* not used yet */
 };
 
 struct cdrom_device_ops {
@@ -413,6 +699,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: */
@@ -420,6 +709,7 @@
 
 extern int register_cdrom(struct cdrom_device_info *cdi);
 extern int unregister_cdrom(struct cdrom_device_info *cdi);
+
 typedef struct {
     int data;
     int audio;
@@ -427,7 +717,161 @@
     int xa;
     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);
+extern int cdrom_number_of_slots(struct cdrom_device_info *cdi);
+extern int cdrom_select_disc(struct cdrom_device_info *cdi, int slot);
+extern int cdrom_mode_select(struct cdrom_device_info *cdi,
+			     struct cdrom_generic_command *cgc);
+extern int cdrom_mode_sense(struct cdrom_device_info *cdi,
+			    struct cdrom_generic_command *cgc,
+			    int page_code, int page_control);
+extern void init_cdrom_command(struct cdrom_generic_command *cgc,
+			       void *buffer, int len);
+
+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;
+
+/* The SCSI spec says there could be 256 slots. */
+#define CDROM_MAX_SLOTS	256
+
+struct cdrom_mechstat_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 fault         : 1;
+	__u8 changer_state : 2;
+	__u8 curslot       : 5;
+	__u8 mech_state    : 3;
+	__u8 door_open     : 1;
+	__u8 reserved1     : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 curslot       : 5;
+	__u8 changer_state : 2;
+	__u8 fault         : 1;
+	__u8 reserved1     : 4;
+	__u8 door_open     : 1;
+	__u8 mech_state    : 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	__u8     curlba[3];
+	__u8     nslots;
+	__u8 short slot_tablelen;
+};
+
+
+struct cdrom_slot {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 disc_present : 1;
+	__u8 reserved1    : 6;
+	__u8 change       : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 change       : 1;
+	__u8 reserved1    : 6;
+	__u8 disc_present : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	__u8 reserved2[3];
+};
+
+struct cdrom_changer_info {
+	struct cdrom_mechstat_header hdr;
+	struct cdrom_slot slots[CDROM_MAX_SLOTS];
+};
+
+typedef enum {
+	mechtype_caddy = 0,
+	mechtype_tray  = 1,
+	mechtype_popup = 2,
+	mechtype_individual_changer = 4,
+	mechtype_cartridge_changer  = 5
+} mechtype_t;
+
 #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	Thu Sep  9 12:27:41 1999
+++ linux/include/linux/sysctl.h	Sun Sep 12 16:19:10 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__
