Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
new file mode 100644
index 0000000..33a020f
--- /dev/null
+++ b/drivers/ide/ide-cd.c
@@ -0,0 +1,3524 @@
+/*
+ * linux/drivers/ide/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-2000  Jens Axboe <axboe@suse.de>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * ATAPI CD-ROM driver.  To be used with ide.c.
+ * See Documentation/cdrom/ide-cd for usage information.
+ *
+ * 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 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_atapi/spec/SFF8020-r2.6/PS/8020r26.ps
+ * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r10.pdf
+ *
+ * Drives that deviate from these standards will be accommodated 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...
+ *
+ * ----------------------------------
+ * TO DO LIST:
+ * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
+ *   boot
+ *
+ * ----------------------------------
+ * 1.00  Oct 31, 1994 -- Initial version.
+ * 1.01  Nov  2, 1994 -- Fixed problem with starting request in
+ *                       cdrom_check_status.
+ * 1.03  Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
+ * (from mlord)       -- minor changes to cdrom_setup()
+ *                    -- renamed ide_dev_s to ide_drive_t, enable irq on command
+ * 2.00  Nov 27, 1994 -- Generalize packet command interface;
+ *                       add audio ioctls.
+ * 2.01  Dec  3, 1994 -- Rework packet command interface to handle devices
+ *                       which send an interrupt when ready for a command.
+ * 2.02  Dec 11, 1994 -- Cache the TOC in the driver.
+ *                       Don't use SCMD_PLAYAUDIO_TI; it's not included
+ *                       in the current version of ATAPI.
+ *                       Try to use LBA instead of track or MSF addressing
+ *                       when possible.
+ *                       Don't wait for READY_STAT.
+ * 2.03  Jan 10, 1995 -- Rewrite block read routines to handle block sizes
+ *                       other than 2k and to move multiple sectors in a
+ *                       single transaction.
+ * 2.04  Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
+ *                       Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
+ *                       help in figuring this out.  Ditto for Acer and
+ *                       Aztech drives, which seem to have the same problem.
+ * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
+ * 2.05  Jun  8, 1995 -- Don't attempt to retry after an illegal request
+ *                        or data protect error.
+ *                       Use HWIF and DEV_HWIF macros as in ide.c.
+ *                       Always try to do a request_sense after
+ *                        a failed command.
+ *                       Include an option to give textual descriptions
+ *                        of ATAPI errors.
+ *                       Fix a bug in handling the sector cache which
+ *                        showed up if the drive returned data in 512 byte
+ *                        blocks (like Pioneer drives).  Thanks to
+ *                        Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ *                       Properly supply the page number field in the
+ *                        MODE_SELECT command.
+ *                       PLAYAUDIO12 is broken on the Aztech; work around it.
+ * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
+ *                       (my apologies to Scott, but now ide-cd.c is independent)
+ * 3.00  Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
+ *                       Implement CDROMREADAUDIO ioctl (UNTESTED).
+ *                       Use input_ide_data() and output_ide_data().
+ *                       Add door locking.
+ *                       Fix usage count leak in cdrom_open, which happened
+ *                        when a read-write mount was attempted.
+ *                       Try to load the disk on open.
+ *                       Implement CDROMEJECT_SW ioctl (off by default).
+ *                       Read total cdrom capacity during open.
+ *                       Rearrange logic in cdrom_decode_status.  Issue
+ *                        request sense commands for failed packet commands
+ *                        from here instead of from cdrom_queue_packet_command.
+ *                        Fix a race condition in retrieving error information.
+ *                       Suppress printing normal unit attention errors and
+ *                        some drive not ready errors.
+ *                       Implement CDROMVOLREAD ioctl.
+ *                       Implement CDROMREADMODE1/2 ioctls.
+ *                       Fix race condition in setting up interrupt handlers
+ *                        when the `serialize' option is used.
+ * 3.01  Sep  2, 1995 -- Fix ordering of reenabling interrupts in
+ *                        cdrom_queue_request.
+ *                       Another try at using ide_[input,output]_data.
+ * 3.02  Sep 16, 1995 -- Stick total disk capacity in partition table as well.
+ *                       Make VERBOSE_IDE_CD_ERRORS dump failed command again.
+ *                       Dump out more information for ILLEGAL REQUEST errs.
+ *                       Fix handling of errors occurring before the
+ *                        packet command is transferred.
+ *                       Fix transfers with odd bytelengths.
+ * 3.03  Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
+ *                       `DCI-2S10' drives are broken too.
+ * 3.04  Nov 20, 1995 -- So are Vertos drives.
+ * 3.05  Dec  1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
+ * 3.06  Dec 16, 1995 -- Add support needed for partitions.
+ *                       More workarounds for Vertos bugs (based on patches
+ *                        from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ *                       Try to eliminate byteorder assumptions.
+ *                       Use atapi_cdrom_subchnl struct definition.
+ *                       Add STANDARD_ATAPI compilation option.
+ * 3.07  Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
+ *                        Vertos 300.
+ *                       Add NO_DOOR_LOCKING configuration option.
+ *                       Handle drive_cmd requests w/NULL args (for hdparm -t).
+ *                       Work around sporadic Sony55e audio play problem.
+ * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
+ *                        problem with "hde=cdrom" with no drive present.  -ml
+ * 3.08  Mar  6, 1996 -- More Vertos workarounds.
+ * 3.09  Apr  5, 1996 -- Add CDROMCLOSETRAY ioctl.
+ *                       Switch to using MSF addressing for audio commands.
+ *                       Reformat to match kernel tabbing style.
+ *                       Add CDROM_GET_UPC ioctl.
+ * 3.10  Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI.
+ * 3.11  Apr 29, 1996 -- Patch from Heiko Eissfeldt <heiko@colossus.escape.de>
+ *                       to remove redundant verify_area calls.
+ * 3.12  May  7, 1996 -- Rudimentary changer support.  Based on patches
+ *                        from Gerhard Zuber <zuber@berlin.snafu.de>.
+ *                       Let open succeed even if there's no loaded disc.
+ * 3.13  May 19, 1996 -- Fixes for changer code.
+ * 3.14  May 29, 1996 -- Add work-around for Vertos 600.
+ *                        (From Hennus Bergman <hennus@sky.ow.nl>.)
+ * 3.15  July 2, 1996 -- Added support for Sanyo 3 CD changers
+ *                        from Ben Galliart <bgallia@luc.edu> with 
+ *                        special help from Jeff Lightfoot 
+ *                        <jeffml@pobox.com>
+ * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification
+ * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
+ * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
+ *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
+ * 3.18  Oct 31, 1996 -- Added module and DMA support.
+ *                       
+ *                       
+ * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
+ *                                 Erik B. Andersen <andersee@debian.org>
+ *                     -- Newer Creative drives don't always set the error
+ *                          register correctly.  Make sure we see media changes
+ *                          regardless.
+ *                     -- Integrate with generic cdrom driver.
+ *                     -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
+ *                          a patch from Ciro Cattuto <>.
+ *                     -- Call set_device_ro.
+ *                     -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ *                          ioctls, based on patch by Erik Andersen
+ *                     -- Add some probes of drive capability during setup.
+ *
+ * 4.01  Nov 11, 1996  -- Split into ide-cd.c and ide-cd.h
+ *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE 
+ *                          ioctls in favor of a generalized approach 
+ *                          using the generic cdrom driver.
+ *                     -- Fully integrated with the 2.1.X kernel.
+ *                     -- Other stuff that I forgot (lots of changes)
+ *
+ * 4.02  Dec 01, 1996  -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
+ *                          to fix the drive door locking problems.
+ *
+ * 4.03  Dec 04, 1996  -- Added DSC overlap support.
+ * 4.04  Dec 29, 1996  -- Added CDROMREADRAW ioclt based on patch 
+ *                          by Ales Makarov (xmakarov@sun.felk.cvut.cz)
+ *
+ * 4.05  Nov 20, 1997  -- Modified to print more drive info on init
+ *                        Minor other changes
+ *                        Fix errors on CDROMSTOP (If you have a "Dolphin",
+ *                          you must define IHAVEADOLPHIN)
+ *                        Added identifier so new Sanyo CD-changer works
+ *                        Better detection if door locking isn't supported
+ *
+ * 4.06  Dec 17, 1997  -- fixed endless "tray open" messages  -ml
+ * 4.07  Dec 17, 1997  -- fallback to set pc->stat on "tray open"
+ * 4.08  Dec 18, 1997  -- spew less noise when tray is empty
+ *                     -- fix speed display for ACER 24X, 18X
+ * 4.09  Jan 04, 1998  -- fix handling of the last block so we return
+ *                         an end of file instead of an I/O error (Gadi)
+ * 4.10  Jan 24, 1998  -- fixed a bug so now changers can change to a new
+ *                         slot when there is no disc in the current slot.
+ *                     -- Fixed a memory leak where info->changer_info was
+ *                         malloc'ed but never free'd when closing the device.
+ *                     -- Cleaned up the global namespace a bit by making more
+ *                         functions static that should already have been.
+ * 4.11  Mar 12, 1998  -- Added support for the CDROM_SELECT_SPEED ioctl
+ *                         based on a patch for 2.0.33 by Jelle Foks 
+ *                         <jelle@scintilla.utwente.nl>, a patch for 2.0.33
+ *                         by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
+ *                         version, and my own efforts.  -erik
+ *                     -- Fixed a stupid bug which egcs was kind enough to
+ *                         inform me of where "Illegal mode for this track"
+ *                         was never returned due to a comparison on data
+ *                         types of limited range.
+ * 4.12  Mar 29, 1998  -- Fixed bug in CDROM_SELECT_SPEED so write speed is 
+ *                         now set ionly for CD-R and CD-RW drives.  I had 
+ *                         removed this support because it produced errors.
+ *                         It produced errors _only_ for non-writers. duh.
+ * 4.13  May 05, 1998  -- Suppress useless "in progress of becoming ready"
+ *                         messages, since this is not an error.
+ *                     -- Change error messages to be const
+ *                     -- Remove a "\t" which looks ugly in the syslogs
+ * 4.14  July 17, 1998 -- Change to pointing to .ps version of ATAPI spec
+ *                         since the .pdf version doesn't seem to work...
+ *                     -- Updated the TODO list to something more current.
+ *
+ * 4.15  Aug 25, 1998  -- Updated ide-cd.h to respect mechine endianess, 
+ *                         patch thanks to "Eddie C. Dost" <ecd@skynet.be>
+ *
+ * 4.50  Oct 19, 1998  -- New maintainers!
+ *                         Jens Axboe <axboe@image.dk>
+ *                         Chris Zwilling <chris@cloudnet.com>
+ *
+ * 4.51  Dec 23, 1998  -- Jens Axboe <axboe@image.dk>
+ *                      - ide_cdrom_reset enabled since the ide subsystem
+ *                         handles resets fine now. <axboe@image.dk>
+ *                      - Transfer size fix for Samsung CD-ROMs, thanks to
+ *                        "Ville Hallik" <ville.hallik@mail.ee>.
+ *                      - other minor stuff.
+ *
+ * 4.52  Jan 19, 1999  -- Jens Axboe <axboe@image.dk>
+ *                      - Detect DVD-ROM/RAM drives
+ *
+ * 4.53  Feb 22, 1999   - Include other model Samsung and one Goldstar
+ *                         drive in transfer size limit.
+ *                      - Fix the I/O error when doing eject without a medium
+ *                         loaded on some drives.
+ *                      - CDROMREADMODE2 is now implemented through
+ *                         CDROMREADRAW, since many drives don't support
+ *                         MODE2 (even though ATAPI 2.6 says they must).
+ *                      - Added ignore parameter to ide-cd (as a module), eg
+ *                         	insmod ide-cd ignore='hda hdb'
+ *                         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 occurred.
+ *			- 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 
+ *
+ * 4.57  Apr 7, 2000	- Fixed sense reporting.
+ *			- Fixed possible oops in ide_cdrom_get_last_session()
+ *			- Fix locking mania and make ide_cdrom_reset relock
+ *			- Stop spewing errors to log when magicdev polls with
+ *			  TEST_UNIT_READY on some drives.
+ *			- Various fixes from Tobias Ringstrom:
+ *			  tray if it was locked prior to the reset.
+ *			  - cdrom_read_capacity returns one frame too little.
+ *			  - Fix real capacity reporting.
+ *
+ * 4.58  May 1, 2000	- Clean up ACER50 stuff.
+ *			- Fix small problem with ide_cdrom_capacity
+ *
+ * 4.59  Aug 11, 2000	- Fix changer problem in cdrom_read_toc, we weren't
+ *			  correctly sensing a disc change.
+ *			- Rearranged some code
+ *			- Use extended sense on drives that support it for
+ *			  correctly reporting tray status -- from
+ *			  Michael D Johnson <johnsom@orst.edu>
+ * 4.60  Dec 17, 2003	- Add mt rainier support
+ *			- Bump timeout for packet commands, matches sr
+ *			- Odd stuff
+ * 4.61  Jan 22, 2004	- support hardware sector sizes other than 2kB,
+ *			  Pascal Schmidt <der.eremit@email.de>
+ *
+ *************************************************************************/
+ 
+#define IDECD_VERSION "4.61"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+#include <linux/completion.h>
+
+#include <scsi/scsi.h>	/* For SCSI -> ATAPI command conversion */
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "ide-cd.h"
+
+static DECLARE_MUTEX(idecd_ref_sem);
+
+#define to_ide_cd(obj) container_of(obj, struct cdrom_info, kref) 
+
+#define ide_cd_g(disk) \
+	container_of((disk)->private_data, struct cdrom_info, driver)
+
+static struct cdrom_info *ide_cd_get(struct gendisk *disk)
+{
+	struct cdrom_info *cd = NULL;
+
+	down(&idecd_ref_sem);
+	cd = ide_cd_g(disk);
+	if (cd)
+		kref_get(&cd->kref);
+	up(&idecd_ref_sem);
+	return cd;
+}
+
+static void ide_cd_release(struct kref *);
+
+static void ide_cd_put(struct cdrom_info *cd)
+{
+	down(&idecd_ref_sem);
+	kref_put(&cd->kref, ide_cd_release);
+	up(&idecd_ref_sem);
+}
+
+/****************************************************************************
+ * Generic packet command support and error handling routines.
+ */
+
+/* Mark that we've seen a media change, and invalidate our internal
+   buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
+	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+	info->nsectors_buffered = 0;
+}
+
+static int cdrom_log_sense(ide_drive_t *drive, struct request *rq,
+			   struct request_sense *sense)
+{
+	int log = 0;
+
+	if (!sense || !rq || (rq->flags & REQ_QUIET))
+		return 0;
+
+	switch (sense->sense_key) {
+		case NO_SENSE: case RECOVERED_ERROR:
+			break;
+		case NOT_READY:
+			/*
+			 * don't care about tray state messages for
+			 * e.g. capacity commands or in-progress or
+			 * becoming ready
+			 */
+			if (sense->asc == 0x3a || sense->asc == 0x04)
+				break;
+			log = 1;
+			break;
+		case ILLEGAL_REQUEST:
+			/*
+			 * don't log START_STOP unit with LoEj set, since
+			 * we cannot reliably check if drive can auto-close
+			 */
+			if (rq->cmd[0] == GPCMD_START_STOP_UNIT && sense->asc == 0x24)
+				log = 0;
+			break;
+		case UNIT_ATTENTION:
+			/*
+			 * Make good and sure we've seen this potential media
+			 * change. Some drives (i.e. Creative) fail to present
+			 * the correct sense key in the error register.
+			 */
+			cdrom_saw_media_change(drive);
+			break;
+		default:
+			log = 1;
+			break;
+	}
+	return log;
+}
+
+static
+void cdrom_analyze_sense_data(ide_drive_t *drive,
+			      struct request *failed_command,
+			      struct request_sense *sense)
+{
+	if (!cdrom_log_sense(drive, failed_command, sense))
+		return;
+
+	/*
+	 * If a read toc is executed for a CD-R or CD-RW medium where
+	 * the first toc has not been recorded yet, it will fail with
+	 * 05/24/00 (which is a confusing error)
+	 */
+	if (failed_command && failed_command->cmd[0] == GPCMD_READ_TOC_PMA_ATIP)
+		if (sense->sense_key == 0x05 && sense->asc == 0x24)
+			return;
+
+#if VERBOSE_IDE_CD_ERRORS
+	{
+		int i;
+		const char *s;
+		char buf[80];
+
+		printk ("ATAPI device %s:\n", drive->name);
+		if (sense->error_code==0x70)
+			printk("  Error: ");
+		else if (sense->error_code==0x71)
+			printk("  Deferred Error: ");
+		else if (sense->error_code == 0x7f)
+			printk("  Vendor-specific Error: ");
+		else
+			printk("  Unknown Error Type: ");
+
+		if (sense->sense_key < ARY_LEN(sense_key_texts))
+			s = sense_key_texts[sense->sense_key];
+		else
+			s = "bad sense key!";
+
+		printk("%s -- (Sense key=0x%02x)\n", s, sense->sense_key);
+
+		if (sense->asc == 0x40) {
+			sprintf(buf, "Diagnostic failure on component 0x%02x",
+				 sense->ascq);
+			s = buf;
+		} else {
+			int lo = 0, mid, hi = ARY_LEN(sense_data_texts);
+			unsigned long key = (sense->sense_key << 16);
+			key |= (sense->asc << 8);
+			if (!(sense->ascq >= 0x80 && sense->ascq <= 0xdd))
+				key |= sense->ascq;
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (sense_data_texts[mid].asc_ascq == key ||
+				    sense_data_texts[mid].asc_ascq == (0xff0000|key)) {
+					s = sense_data_texts[mid].text;
+					break;
+				}
+				else if (sense_data_texts[mid].asc_ascq > key)
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+		}
+
+		if (s == NULL) {
+			if (sense->asc > 0x80)
+				s = "(vendor-specific error)";
+			else
+				s = "(reserved error code)";
+		}
+
+		printk(KERN_ERR "  %s -- (asc=0x%02x, ascq=0x%02x)\n",
+			s, sense->asc, sense->ascq);
+
+		if (failed_command != NULL) {
+
+			int lo=0, mid, hi= ARY_LEN (packet_command_texts);
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (packet_command_texts[mid].packet_command ==
+				    failed_command->cmd[0]) {
+					s = packet_command_texts[mid].text;
+					break;
+				}
+				if (packet_command_texts[mid].packet_command >
+				    failed_command->cmd[0])
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+
+			printk (KERN_ERR "  The failed \"%s\" packet command was: \n  \"", s);
+			for (i=0; i<sizeof (failed_command->cmd); i++)
+				printk ("%02x ", failed_command->cmd[i]);
+			printk ("\"\n");
+		}
+
+		/* The SKSV bit specifies validity of the sense_key_specific
+		 * in the next two commands. It is bit 7 of the first byte.
+		 * In the case of NOT_READY, if SKSV is set the drive can
+		 * give us nice ETA readings.
+		 */
+		if (sense->sense_key == NOT_READY && (sense->sks[0] & 0x80)) {
+			int progress = (sense->sks[1] << 8 | sense->sks[2]) * 100;
+			printk(KERN_ERR "  Command is %02d%% complete\n", progress / 0xffff);
+
+		}
+
+		if (sense->sense_key == ILLEGAL_REQUEST &&
+		    (sense->sks[0] & 0x80) != 0) {
+			printk(KERN_ERR "  Error in %s byte %d",
+				(sense->sks[0] & 0x40) != 0 ?
+				"command packet" : "command data",
+				(sense->sks[1] << 8) + sense->sks[2]);
+
+			if ((sense->sks[0] & 0x40) != 0)
+				printk (" bit %d", sense->sks[0] & 0x07);
+
+			printk ("\n");
+		}
+	}
+
+#else /* not VERBOSE_IDE_CD_ERRORS */
+
+	/* Suppress printing unit attention and `in progress of becoming ready'
+	   errors when we're not being verbose. */
+
+	if (sense->sense_key == UNIT_ATTENTION ||
+	    (sense->sense_key == NOT_READY && (sense->asc == 4 ||
+						sense->asc == 0x3a)))
+		return;
+
+	printk(KERN_ERR "%s: error code: 0x%02x  sense_key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
+		drive->name,
+		sense->error_code, sense->sense_key,
+		sense->asc, sense->ascq);
+#endif /* not VERBOSE_IDE_CD_ERRORS */
+}
+
+/*
+ * Initialize a ide-cd packet command request
+ */
+static void cdrom_prepare_request(ide_drive_t *drive, struct request *rq)
+{
+	struct cdrom_info *cd = drive->driver_data;
+
+	ide_init_drive_cmd(rq);
+	rq->flags = REQ_PC;
+	rq->rq_disk = cd->disk;
+}
+
+static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense,
+				      struct request *failed_command)
+{
+	struct cdrom_info *info		= drive->driver_data;
+	struct request *rq		= &info->request_sense_request;
+
+	if (sense == NULL)
+		sense = &info->sense_data;
+
+	/* stuff the sense request in front of our current request */
+	cdrom_prepare_request(drive, rq);
+
+	rq->data = sense;
+	rq->cmd[0] = GPCMD_REQUEST_SENSE;
+	rq->cmd[4] = rq->data_len = 18;
+
+	rq->flags = REQ_SENSE;
+
+	/* NOTE! Save the failed command in "rq->buffer" */
+	rq->buffer = (void *) failed_command;
+
+	(void) ide_do_drive_cmd(drive, rq, ide_preempt);
+}
+
+static void cdrom_end_request (ide_drive_t *drive, int uptodate)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	int nsectors = rq->hard_cur_sectors;
+
+	if ((rq->flags & REQ_SENSE) && uptodate) {
+		/*
+		 * For REQ_SENSE, "rq->buffer" points to the original failed
+		 * request
+		 */
+		struct request *failed = (struct request *) rq->buffer;
+		struct cdrom_info *info = drive->driver_data;
+		void *sense = &info->sense_data;
+		unsigned long flags;
+
+		if (failed) {
+			if (failed->sense) {
+				sense = failed->sense;
+				failed->sense_len = rq->sense_len;
+			}
+
+			/*
+			 * now end failed request
+			 */
+			spin_lock_irqsave(&ide_lock, flags);
+			end_that_request_chunk(failed, 0, failed->data_len);
+			end_that_request_last(failed);
+			spin_unlock_irqrestore(&ide_lock, flags);
+		}
+
+		cdrom_analyze_sense_data(drive, failed, sense);
+	}
+
+	if (!rq->current_nr_sectors && blk_fs_request(rq))
+		uptodate = 1;
+	/* make sure it's fully ended */
+	if (blk_pc_request(rq))
+		nsectors = (rq->data_len + 511) >> 9;
+	if (!nsectors)
+		nsectors = 1;
+
+	ide_end_request(drive, uptodate, nsectors);
+}
+
+/* Returns 0 if the request should be continued.
+   Returns 1 if the request was ended. */
+static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	int stat, err, sense_key;
+	
+	/* Check for errors. */
+	stat = HWIF(drive)->INB(IDE_STATUS_REG);
+	if (stat_ret)
+		*stat_ret = stat;
+
+	if (OK_STAT(stat, good_stat, BAD_R_STAT))
+		return 0;
+
+	/* Get the IDE error register. */
+	err = HWIF(drive)->INB(IDE_ERROR_REG);
+	sense_key = err >> 4;
+
+	if (rq == NULL) {
+		printk("%s: missing rq in cdrom_decode_status\n", drive->name);
+		return 1;
+	}
+
+	if (rq->flags & REQ_SENSE) {
+		/* We got an error trying to get sense info
+		   from the drive (probably while trying
+		   to recover from a former error).  Just give up. */
+
+		rq->flags |= REQ_FAILED;
+		cdrom_end_request(drive, 0);
+		ide_error(drive, "request sense failure", stat);
+		return 1;
+
+	} else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) {
+		/* All other functions, except for READ. */
+		unsigned long flags;
+
+		/*
+		 * if we have an error, pass back CHECK_CONDITION as the
+		 * scsi status byte
+		 */
+		if ((rq->flags & REQ_BLOCK_PC) && !rq->errors)
+			rq->errors = SAM_STAT_CHECK_CONDITION;
+
+		/* Check for tray open. */
+		if (sense_key == NOT_READY) {
+			cdrom_saw_media_change (drive);
+		} else if (sense_key == UNIT_ATTENTION) {
+			/* Check for media change. */
+			cdrom_saw_media_change (drive);
+			/*printk("%s: media changed\n",drive->name);*/
+			return 0;
+		} else if (!(rq->flags & REQ_QUIET)) {
+			/* Otherwise, print an error. */
+			ide_dump_status(drive, "packet command error", stat);
+		}
+		
+		rq->flags |= REQ_FAILED;
+
+		/*
+		 * instead of playing games with moving completions around,
+		 * remove failed request completely and end it when the
+		 * request sense has completed
+		 */
+		if (stat & ERR_STAT) {
+			spin_lock_irqsave(&ide_lock, flags);
+			blkdev_dequeue_request(rq);
+			HWGROUP(drive)->rq = NULL;
+			spin_unlock_irqrestore(&ide_lock, flags);
+
+			cdrom_queue_request_sense(drive, rq->sense, rq);
+		} else
+			cdrom_end_request(drive, 0);
+
+	} else if (blk_fs_request(rq)) {
+		int do_end_request = 0;
+
+		/* Handle errors from READ and WRITE requests. */
+
+		if (blk_noretry_request(rq))
+			do_end_request = 1;
+
+		if (sense_key == NOT_READY) {
+			/* Tray open. */
+			if (rq_data_dir(rq) == READ) {
+				cdrom_saw_media_change (drive);
+
+				/* Fail the request. */
+				printk ("%s: tray open\n", drive->name);
+				do_end_request = 1;
+			} else {
+				struct cdrom_info *info = drive->driver_data;
+
+				/* allow the drive 5 seconds to recover, some
+				 * devices will return this error while flushing
+				 * data from cache */
+				if (!rq->errors)
+					info->write_timeout = jiffies + ATAPI_WAIT_WRITE_BUSY;
+				rq->errors = 1;
+				if (time_after(jiffies, info->write_timeout))
+					do_end_request = 1;
+				else {
+					unsigned long flags;
+
+					/*
+					 * take a breather relying on the
+					 * unplug timer to kick us again
+					 */
+					spin_lock_irqsave(&ide_lock, flags);
+					blk_plug_device(drive->queue);
+					spin_unlock_irqrestore(&ide_lock,flags);
+					return 1;
+				}
+			}
+		} else if (sense_key == UNIT_ATTENTION) {
+			/* Media change. */
+			cdrom_saw_media_change (drive);
+
+			/* Arrange to retry the request.
+			   But be sure to give up if we've retried
+			   too many times. */
+			if (++rq->errors > ERROR_MAX)
+				do_end_request = 1;
+		} else if (sense_key == ILLEGAL_REQUEST ||
+			   sense_key == DATA_PROTECT) {
+			/* No point in retrying after an illegal
+			   request or data protect error.*/
+			ide_dump_status (drive, "command error", stat);
+			do_end_request = 1;
+		} else if (sense_key == MEDIUM_ERROR) {
+			/* No point in re-trying a zillion times on a bad 
+			 * sector...  If we got here the error is not correctable */
+			ide_dump_status (drive, "media error (bad sector)", stat);
+			do_end_request = 1;
+		} else if (sense_key == BLANK_CHECK) {
+			/* Disk appears blank ?? */
+			ide_dump_status (drive, "media error (blank)", stat);
+			do_end_request = 1;
+		} else if ((err & ~ABRT_ERR) != 0) {
+			/* Go to the default handler
+			   for other errors. */
+			ide_error(drive, "cdrom_decode_status", stat);
+			return 1;
+		} else if ((++rq->errors > ERROR_MAX)) {
+			/* We've racked up too many retries.  Abort. */
+			do_end_request = 1;
+		}
+
+		if (do_end_request)
+			cdrom_end_request(drive, 0);
+
+		/* If we got a CHECK_CONDITION status,
+		   queue a request sense command. */
+		if ((stat & ERR_STAT) != 0)
+			cdrom_queue_request_sense(drive, NULL, NULL);
+	} else {
+		blk_dump_rq_flags(rq, "ide-cd: bad rq");
+		cdrom_end_request(drive, 0);
+	}
+
+	/* Retry, or handle the next request. */
+	return 1;
+}
+
+static int cdrom_timer_expiry(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned long wait = 0;
+
+	/*
+	 * Some commands are *slow* and normally take a long time to
+	 * complete. Usually we can use the ATAPI "disconnect" to bypass
+	 * this, but not all commands/drives support that. Let
+	 * ide_timer_expiry keep polling us for these.
+	 */
+	switch (rq->cmd[0]) {
+		case GPCMD_BLANK:
+		case GPCMD_FORMAT_UNIT:
+		case GPCMD_RESERVE_RZONE_TRACK:
+		case GPCMD_CLOSE_TRACK:
+		case GPCMD_FLUSH_CACHE:
+			wait = ATAPI_WAIT_PC;
+			break;
+		default:
+			if (!(rq->flags & REQ_QUIET))
+				printk(KERN_INFO "ide-cd: cmd 0x%x timed out\n", rq->cmd[0]);
+			wait = 0;
+			break;
+	}
+	return wait;
+}
+
+/* Set up the device registers for transferring a packet command on DEV,
+   expecting to later transfer XFERLEN bytes.  HANDLER is the routine
+   which actually transfers the command to the drive.  If this is a
+   drq_interrupt device, this routine will arrange for HANDLER to be
+   called when the interrupt from the drive arrives.  Otherwise, HANDLER
+   will be called immediately after the drive is prepared for the transfer. */
+
+static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive,
+						  int xferlen,
+						  ide_handler_t *handler)
+{
+	ide_startstop_t startstop;
+	struct cdrom_info *info = drive->driver_data;
+	ide_hwif_t *hwif = drive->hwif;
+
+	/* Wait for the controller to be idle. */
+	if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY))
+		return startstop;
+
+	if (info->dma)
+		info->dma = !hwif->dma_setup(drive);
+
+	/* Set up the controller registers. */
+	/* FIXME: for Virtual DMA we must check harder */
+	HWIF(drive)->OUTB(info->dma, IDE_FEATURE_REG);
+	HWIF(drive)->OUTB(0, IDE_IREASON_REG);
+	HWIF(drive)->OUTB(0, IDE_SECTOR_REG);
+
+	HWIF(drive)->OUTB(xferlen & 0xff, IDE_BCOUNTL_REG);
+	HWIF(drive)->OUTB(xferlen >> 8  , IDE_BCOUNTH_REG);
+	if (IDE_CONTROL_REG)
+		HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG);
+ 
+	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+		/* packet command */
+		ide_execute_command(drive, WIN_PACKETCMD, handler, ATAPI_WAIT_PC, cdrom_timer_expiry);
+		return ide_started;
+	} else {
+		unsigned long flags;
+
+		/* packet command */
+		spin_lock_irqsave(&ide_lock, flags);
+		hwif->OUTBSYNC(drive, WIN_PACKETCMD, IDE_COMMAND_REG);
+		ndelay(400);
+		spin_unlock_irqrestore(&ide_lock, flags);
+
+		return (*handler) (drive);
+	}
+}
+
+/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
+   The device registers must have already been prepared
+   by cdrom_start_packet_command.
+   HANDLER is the interrupt handler to call when the command completes
+   or there's data ready. */
+/*
+ * changed 5 parameters to 3 for dvd-ram
+ * struct packet_command *pc; now packet_command_t *pc;
+ */
+#define ATAPI_MIN_CDB_BYTES 12
+static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
+					  struct request *rq,
+					  ide_handler_t *handler)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	int cmd_len;
+	struct cdrom_info *info = drive->driver_data;
+	ide_startstop_t startstop;
+
+	if (CDROM_CONFIG_FLAGS(drive)->drq_interrupt) {
+		/* Here we should have been called after receiving an interrupt
+		   from the device.  DRQ should how be set. */
+
+		/* Check for errors. */
+		if (cdrom_decode_status(drive, DRQ_STAT, NULL))
+			return ide_stopped;
+	} else {
+		/* Otherwise, we must wait for DRQ to get set. */
+		if (ide_wait_stat(&startstop, drive, DRQ_STAT,
+				BUSY_STAT, WAIT_READY))
+			return startstop;
+	}
+
+	/* Arm the interrupt handler. */
+	ide_set_handler(drive, handler, rq->timeout, cdrom_timer_expiry);
+
+	/* ATAPI commands get padded out to 12 bytes minimum */
+	cmd_len = COMMAND_SIZE(rq->cmd[0]);
+	if (cmd_len < ATAPI_MIN_CDB_BYTES)
+		cmd_len = ATAPI_MIN_CDB_BYTES;
+
+	/* Send the command to the device. */
+	HWIF(drive)->atapi_output_bytes(drive, rq->cmd, cmd_len);
+
+	/* Start the DMA if need be */
+	if (info->dma)
+		hwif->dma_start(drive);
+
+	return ide_started;
+}
+
+/****************************************************************************
+ * Block read functions.
+ */
+
+/*
+ * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
+ * buffer.  Once the first sector is added, any subsequent sectors are
+ * assumed to be continuous (until the buffer is cleared).  For the first
+ * sector added, SECTOR is its sector number.  (SECTOR is then ignored until
+ * the buffer is cleared.)
+ */
+static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
+                                  int sectors_to_transfer)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	/* Number of sectors to read into the buffer. */
+	int sectors_to_buffer = min_t(int, sectors_to_transfer,
+				     (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+				       info->nsectors_buffered);
+
+	char *dest;
+
+	/* If we couldn't get a buffer, don't try to buffer anything... */
+	if (info->buffer == NULL)
+		sectors_to_buffer = 0;
+
+	/* If this is the first sector in the buffer, remember its number. */
+	if (info->nsectors_buffered == 0)
+		info->sector_buffered = sector;
+
+	/* Read the data into the buffer. */
+	dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE;
+	while (sectors_to_buffer > 0) {
+		HWIF(drive)->atapi_input_bytes(drive, dest, SECTOR_SIZE);
+		--sectors_to_buffer;
+		--sectors_to_transfer;
+		++info->nsectors_buffered;
+		dest += SECTOR_SIZE;
+	}
+
+	/* Throw away any remaining data. */
+	while (sectors_to_transfer > 0) {
+		static char dum[SECTOR_SIZE];
+		HWIF(drive)->atapi_input_bytes(drive, dum, sizeof (dum));
+		--sectors_to_transfer;
+	}
+}
+
+/*
+ * Check the contents of the interrupt reason register from the cdrom
+ * and attempt to recover if there are problems.  Returns  0 if everything's
+ * ok; nonzero if the request has been terminated.
+ */
+static inline
+int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
+{
+	if (ireason == 2)
+		return 0;
+	else if (ireason == 0) {
+		/* Whoops... The drive is expecting to receive data from us! */
+		printk(KERN_ERR "%s: read_intr: Drive wants to transfer data the "
+						"wrong way!\n", drive->name);
+
+		/* Throw some data at the drive so it doesn't hang
+		   and quit this request. */
+		while (len > 0) {
+			int dum = 0;
+			HWIF(drive)->atapi_output_bytes(drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+	} else  if (ireason == 1) {
+		/* Some drives (ASUS) seem to tell us that status
+		 * info is available. just get it and ignore.
+		 */
+		(void) HWIF(drive)->INB(IDE_STATUS_REG);
+		return 0;
+	} else {
+		/* Drive wants a command packet, or invalid ireason... */
+		printk(KERN_ERR "%s: read_intr: bad interrupt reason %x\n", drive->name,
+								ireason);
+	}
+
+	cdrom_end_request(drive, 0);
+	return -1;
+}
+
+/*
+ * Interrupt routine.  Called when a read request has completed.
+ */
+static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
+{
+	int stat;
+	int ireason, len, sectors_to_transfer, nskip;
+	struct cdrom_info *info = drive->driver_data;
+	u8 lowcyl = 0, highcyl = 0;
+	int dma = info->dma, dma_error = 0;
+
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/*
+	 * handle dma case
+	 */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->ide_dma_end(drive)))
+			__ide_dma_off(drive);
+	}
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	if (dma) {
+		if (!dma_error) {
+			ide_end_request(drive, 1, rq->nr_sectors);
+			return ide_stopped;
+		} else
+			return ide_error(drive, "dma error", stat);
+	}
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG) & 0x3;
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+
+	/* If DRQ is clear, the command has completed. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* If we're not done filling the current buffer, complain.
+		   Otherwise, complete the command normally. */
+		if (rq->current_nr_sectors > 0) {
+			printk (KERN_ERR "%s: cdrom_read_intr: data underrun (%d blocks)\n",
+				drive->name, rq->current_nr_sectors);
+			rq->flags |= REQ_FAILED;
+			cdrom_end_request(drive, 0);
+		} else
+			cdrom_end_request(drive, 1);
+		return ide_stopped;
+	}
+
+	/* Check that the drive is expecting to do the same thing we are. */
+	if (cdrom_read_check_ireason (drive, len, ireason))
+		return ide_stopped;
+
+	/* Assume that the drive will always provide data in multiples
+	   of at least SECTOR_SIZE, as it gets hairy to keep track
+	   of the transfers otherwise. */
+	if ((len % SECTOR_SIZE) != 0) {
+		printk (KERN_ERR "%s: cdrom_read_intr: Bad transfer size %d\n",
+			drive->name, len);
+		if (CDROM_CONFIG_FLAGS(drive)->limit_nframes)
+			printk (KERN_ERR "  This drive is not supported by this version of the driver\n");
+		else {
+			printk (KERN_ERR "  Trying to limit transfer sizes\n");
+			CDROM_CONFIG_FLAGS(drive)->limit_nframes = 1;
+		}
+		cdrom_end_request(drive, 0);
+		return ide_stopped;
+	}
+
+	/* The number of sectors we need to read from the drive. */
+	sectors_to_transfer = len / SECTOR_SIZE;
+
+	/* First, figure out if we need to bit-bucket
+	   any of the leading sectors. */
+	nskip = min_t(int, rq->current_nr_sectors - bio_cur_sectors(rq->bio), sectors_to_transfer);
+
+	while (nskip > 0) {
+		/* We need to throw away a sector. */
+		static char dum[SECTOR_SIZE];
+		HWIF(drive)->atapi_input_bytes(drive, dum, sizeof (dum));
+
+		--rq->current_nr_sectors;
+		--nskip;
+		--sectors_to_transfer;
+	}
+
+	/* Now loop while we still have data to read from the drive. */
+	while (sectors_to_transfer > 0) {
+		int this_transfer;
+
+		/* If we've filled the present buffer but there's another
+		   chained buffer after it, move on. */
+		if (rq->current_nr_sectors == 0 && rq->nr_sectors)
+			cdrom_end_request(drive, 1);
+
+		/* If the buffers are full, cache the rest of the data in our
+		   internal buffer. */
+		if (rq->current_nr_sectors == 0) {
+			cdrom_buffer_sectors(drive, rq->sector, sectors_to_transfer);
+			sectors_to_transfer = 0;
+		} else {
+			/* Transfer data to the buffers.
+			   Figure out how many sectors we can transfer
+			   to the current buffer. */
+			this_transfer = min_t(int, sectors_to_transfer,
+					     rq->current_nr_sectors);
+
+			/* Read this_transfer sectors
+			   into the current buffer. */
+			while (this_transfer > 0) {
+				HWIF(drive)->atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE);
+				rq->buffer += SECTOR_SIZE;
+				--rq->nr_sectors;
+				--rq->current_nr_sectors;
+				++rq->sector;
+				--this_transfer;
+				--sectors_to_transfer;
+			}
+		}
+	}
+
+	/* Done moving data!  Wait for another interrupt. */
+	ide_set_handler(drive, &cdrom_read_intr, ATAPI_WAIT_PC, NULL);
+	return ide_started;
+}
+
+/*
+ * Try to satisfy some of the current read request from our cached data.
+ * Returns nonzero if the request has been completed, zero otherwise.
+ */
+static int cdrom_read_from_buffer (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned short sectors_per_frame;
+
+	sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/* Can't do anything if there's no buffer. */
+	if (info->buffer == NULL) return 0;
+
+	/* Loop while this request needs data and the next block is present
+	   in our cache. */
+	while (rq->nr_sectors > 0 &&
+	       rq->sector >= info->sector_buffered &&
+	       rq->sector < info->sector_buffered + info->nsectors_buffered) {
+		if (rq->current_nr_sectors == 0)
+			cdrom_end_request(drive, 1);
+
+		memcpy (rq->buffer,
+			info->buffer +
+			(rq->sector - info->sector_buffered) * SECTOR_SIZE,
+			SECTOR_SIZE);
+		rq->buffer += SECTOR_SIZE;
+		--rq->current_nr_sectors;
+		--rq->nr_sectors;
+		++rq->sector;
+	}
+
+	/* If we've satisfied the current request,
+	   terminate it successfully. */
+	if (rq->nr_sectors == 0) {
+		cdrom_end_request(drive, 1);
+		return -1;
+	}
+
+	/* Move on to the next buffer if needed. */
+	if (rq->current_nr_sectors == 0)
+		cdrom_end_request(drive, 1);
+
+	/* If this condition does not hold, then the kluge i use to
+	   represent the number of sectors to skip at the start of a transfer
+	   will fail.  I think that this will never happen, but let's be
+	   paranoid and check. */
+	if (rq->current_nr_sectors < bio_cur_sectors(rq->bio) &&
+	    (rq->sector & (sectors_per_frame - 1))) {
+		printk(KERN_ERR "%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+			drive->name, (long)rq->sector);
+		cdrom_end_request(drive, 0);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Routine to send a read packet command to the drive.
+ * This is usually called directly from cdrom_start_read.
+ * However, for drq_interrupt devices, it is called from an interrupt
+ * when the drive is ready to accept the command.
+ */
+static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned short sectors_per_frame;
+	int nskip;
+
+	sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/* If the requested sector doesn't start on a cdrom block boundary,
+	   we must adjust the start of the transfer so that it does,
+	   and remember to skip the first few sectors.
+	   If the CURRENT_NR_SECTORS field is larger than the size
+	   of the buffer, it will mean that we're to skip a number
+	   of sectors equal to the amount by which CURRENT_NR_SECTORS
+	   is larger than the buffer size. */
+	nskip = rq->sector & (sectors_per_frame - 1);
+	if (nskip > 0) {
+		/* Sanity check... */
+		if (rq->current_nr_sectors != bio_cur_sectors(rq->bio) &&
+			(rq->sector & (sectors_per_frame - 1))) {
+			printk(KERN_ERR "%s: cdrom_start_read_continuation: buffer botch (%u)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request(drive, 0);
+			return ide_stopped;
+		}
+		rq->current_nr_sectors += nskip;
+	}
+
+	/* Set up the command */
+	rq->timeout = ATAPI_WAIT_PC;
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command(drive, rq, &cdrom_read_intr);
+}
+
+
+#define IDECD_SEEK_THRESHOLD	(1000)			/* 1000 blocks */
+#define IDECD_SEEK_TIMER	(5 * WAIT_MIN_SLEEP)	/* 100 ms */
+#define IDECD_SEEK_TIMEOUT	(2 * WAIT_CMD)		/* 20 sec */
+
+static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	int stat;
+	static int retry = 10;
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+	CDROM_CONFIG_FLAGS(drive)->seeking = 1;
+
+	if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) {
+		if (--retry == 0) {
+			/*
+			 * this condition is far too common, to bother
+			 * users about it
+			 */
+			/* printk("%s: disabled DSC seek overlap\n", drive->name);*/ 
+			drive->dsc_overlap = 0;
+		}
+	}
+	return ide_stopped;
+}
+
+static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	sector_t frame = rq->sector;
+
+	sector_div(frame, queue_hardsect_size(drive->queue) >> SECTOR_BITS);
+
+	memset(rq->cmd, 0, sizeof(rq->cmd));
+	rq->cmd[0] = GPCMD_SEEK;
+	put_unaligned(cpu_to_be32(frame), (unsigned int *) &rq->cmd[2]);
+
+	rq->timeout = ATAPI_WAIT_PC;
+	return cdrom_transfer_packet_command(drive, rq, &cdrom_seek_intr);
+}
+
+static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	info->cmd = 0;
+	info->start_seek = jiffies;
+	return cdrom_start_packet_command(drive, 0, cdrom_start_seek_continuation);
+}
+
+/* Fix up a possibly partially-processed request so that we can
+   start it over entirely, or even put it back on the request queue. */
+static void restore_request (struct request *rq)
+{
+	if (rq->buffer != bio_data(rq->bio)) {
+		sector_t n = (rq->buffer - (char *) bio_data(rq->bio)) / SECTOR_SIZE;
+
+		rq->buffer = bio_data(rq->bio);
+		rq->nr_sectors += n;
+		rq->sector -= n;
+	}
+	rq->hard_cur_sectors = rq->current_nr_sectors = bio_cur_sectors(rq->bio);
+	rq->hard_nr_sectors = rq->nr_sectors;
+	rq->hard_sector = rq->sector;
+	rq->q->prep_rq_fn(rq->q, rq);
+}
+
+/*
+ * Start a read request from the CD-ROM.
+ */
+static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	unsigned short sectors_per_frame;
+
+	sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/* We may be retrying this request after an error.  Fix up
+	   any weirdness which might be present in the request packet. */
+	restore_request(rq);
+
+	/* Satisfy whatever we can of this request from our cached sector. */
+	if (cdrom_read_from_buffer(drive))
+		return ide_stopped;
+
+	blk_attempt_remerge(drive->queue, rq);
+
+	/* Clear the local sector buffer. */
+	info->nsectors_buffered = 0;
+
+	/* use dma, if possible. */
+	info->dma = drive->using_dma;
+	if ((rq->sector & (sectors_per_frame - 1)) ||
+	    (rq->nr_sectors & (sectors_per_frame - 1)))
+		info->dma = 0;
+
+	info->cmd = READ;
+
+	/* Start sending the read request to the drive. */
+	return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation);
+}
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Interrupt routine for packet command completion. */
+static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
+{
+	int ireason, len, thislen;
+	struct request *rq = HWGROUP(drive)->rq;
+	u8 lowcyl = 0, highcyl = 0;
+	int stat;
+
+	/* Check for errors. */
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG);
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+
+	/* If DRQ is clear, the command has completed.
+	   Complain if we still have data left to transfer. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* Some of the trailing request sense fields are optional, and
+		   some drives don't send them.  Sigh. */
+		if (rq->cmd[0] == GPCMD_REQUEST_SENSE &&
+		    rq->data_len > 0 &&
+		    rq->data_len <= 5) {
+			while (rq->data_len > 0) {
+				*(unsigned char *)rq->data++ = 0;
+				--rq->data_len;
+			}
+		}
+
+		if (rq->data_len == 0)
+			cdrom_end_request(drive, 1);
+		else {
+			/* Comment this out, because this always happens 
+			   right after a reset occurs, and it is annoying to 
+			   always print expected stuff.  */
+			/*
+			printk ("%s: cdrom_pc_intr: data underrun %d\n",
+				drive->name, pc->buflen);
+			*/
+			rq->flags |= REQ_FAILED;
+			cdrom_end_request(drive, 0);
+		}
+		return ide_stopped;
+	}
+
+	/* Figure out how much data to transfer. */
+	thislen = rq->data_len;
+	if (thislen > len) thislen = len;
+
+	/* The drive wants to be written to. */
+	if ((ireason & 3) == 0) {
+		if (!rq->data) {
+			blk_dump_rq_flags(rq, "cdrom_pc_intr, write");
+			goto confused;
+		}
+		/* Transfer the data. */
+		HWIF(drive)->atapi_output_bytes(drive, rq->data, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			HWIF(drive)->atapi_output_bytes(drive, &dum, sizeof(dum));
+			len -= sizeof(dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		rq->data += thislen;
+		rq->data_len -= thislen;
+	}
+
+	/* Same drill for reading. */
+	else if ((ireason & 3) == 2) {
+		if (!rq->data) {
+			blk_dump_rq_flags(rq, "cdrom_pc_intr, write");
+			goto confused;
+		}
+		/* Transfer the data. */
+		HWIF(drive)->atapi_input_bytes(drive, rq->data, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum));
+			len -= sizeof(dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		rq->data += thislen;
+		rq->data_len -= thislen;
+
+		if (rq->flags & REQ_SENSE)
+			rq->sense_len += thislen;
+	} else {
+confused:
+		printk (KERN_ERR "%s: cdrom_pc_intr: The drive "
+			"appears confused (ireason = 0x%02x)\n",
+			drive->name, ireason);
+		rq->flags |= REQ_FAILED;
+	}
+
+	/* Now we wait for another interrupt. */
+	ide_set_handler(drive, &cdrom_pc_intr, ATAPI_WAIT_PC, cdrom_timer_expiry);
+	return ide_started;
+}
+
+static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+	if (!rq->timeout)
+		rq->timeout = ATAPI_WAIT_PC;
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command(drive, rq, &cdrom_pc_intr);
+}
+
+
+static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
+{
+	int len;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	info->cmd = 0;
+	rq->flags &= ~REQ_FAILED;
+	len = rq->data_len;
+
+	/* Start sending the command to the drive. */
+	return cdrom_start_packet_command(drive, len, cdrom_do_pc_continuation);
+}
+
+
+static
+int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq)
+{
+	struct request_sense sense;
+	int retries = 10;
+	unsigned int flags = rq->flags;
+
+	if (rq->sense == NULL)
+		rq->sense = &sense;
+
+	/* Start of retry loop. */
+	do {
+		int error;
+		unsigned long time = jiffies;
+		rq->flags = flags;
+
+		error = ide_do_drive_cmd(drive, rq, ide_wait);
+		time = jiffies - time;
+
+		/* FIXME: we should probably abort/retry or something 
+		 * in case of failure */
+		if (rq->flags & REQ_FAILED) {
+			/* The request failed.  Retry if it was due to a unit
+			   attention status
+			   (usually means media was changed). */
+			struct request_sense *reqbuf = rq->sense;
+
+			if (reqbuf->sense_key == UNIT_ATTENTION)
+				cdrom_saw_media_change(drive);
+			else if (reqbuf->sense_key == NOT_READY &&
+				 reqbuf->asc == 4 && reqbuf->ascq != 4) {
+				/* The drive is in the process of loading
+				   a disk.  Retry, but wait a little to give
+				   the drive time to complete the load. */
+				ssleep(2);
+			} else {
+				/* Otherwise, don't retry. */
+				retries = 0;
+			}
+			--retries;
+		}
+
+		/* End of retry loop. */
+	} while ((rq->flags & REQ_FAILED) && retries >= 0);
+
+	/* Return an error if the command failed. */
+	return (rq->flags & REQ_FAILED) ? -EIO : 0;
+}
+
+/*
+ * Write handling
+ */
+static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, int ireason)
+{
+	/* Two notes about IDE interrupt reason here - 0 means that
+	 * the drive wants to receive data from us, 2 means that
+	 * the drive is expecting to transfer data to us.
+	 */
+	if (ireason == 0)
+		return 0;
+	else if (ireason == 2) {
+		/* Whoops... The drive wants to send data. */
+		printk(KERN_ERR "%s: write_intr: wrong transfer direction!\n",
+							drive->name);
+
+		while (len > 0) {
+			int dum = 0;
+			HWIF(drive)->atapi_input_bytes(drive, &dum, sizeof(dum));
+			len -= sizeof(dum);
+		}
+	} else {
+		/* Drive wants a command packet, or invalid ireason... */
+		printk(KERN_ERR "%s: write_intr: bad interrupt reason %x\n",
+							drive->name, ireason);
+	}
+
+	cdrom_end_request(drive, 0);
+	return 1;
+}
+
+static void post_transform_command(struct request *req)
+{
+	u8 *c = req->cmd;
+	char *ibuf;
+
+	if (!blk_pc_request(req))
+		return;
+
+	if (req->bio)
+		ibuf = bio_data(req->bio);
+	else
+		ibuf = req->data;
+
+	if (!ibuf)
+		return;
+
+	/*
+	 * set ansi-revision and response data as atapi
+	 */
+	if (c[0] == GPCMD_INQUIRY) {
+		ibuf[2] |= 2;
+		ibuf[3] = (ibuf[3] & 0xf0) | 2;
+	}
+}
+
+typedef void (xfer_func_t)(ide_drive_t *, void *, u32);
+
+/*
+ * best way to deal with dma that is not sector aligned right now... note
+ * that in this path we are not using ->data or ->buffer at all. this irs
+ * can replace cdrom_pc_intr, cdrom_read_intr, and cdrom_write_intr in the
+ * future.
+ */
+static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq = HWGROUP(drive)->rq;
+	int dma_error, dma, stat, ireason, len, thislen;
+	u8 lowcyl, highcyl;
+	xfer_func_t *xferfunc;
+	unsigned long flags;
+
+	/* Check for errors. */
+	dma_error = 0;
+	dma = info->dma;
+	if (dma) {
+		info->dma = 0;
+		dma_error = HWIF(drive)->ide_dma_end(drive);
+	}
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	/*
+	 * using dma, transfer is complete now
+	 */
+	if (dma) {
+		if (dma_error) {
+			printk(KERN_ERR "ide-cd: dma error\n");
+			__ide_dma_off(drive);
+			return ide_error(drive, "dma error", stat);
+		}
+
+		end_that_request_chunk(rq, 1, rq->data_len);
+		rq->data_len = 0;
+		goto end_request;
+	}
+
+	/*
+	 * ok we fall to pio :/
+	 */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG) & 0x3;
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+	thislen = rq->data_len;
+	if (thislen > len)
+		thislen = len;
+
+	/*
+	 * If DRQ is clear, the command has completed.
+	 */
+	if ((stat & DRQ_STAT) == 0)
+		goto end_request;
+
+	/*
+	 * check which way to transfer data
+	 */
+	if (rq_data_dir(rq) == WRITE) {
+		/*
+		 * write to drive
+		 */
+		if (cdrom_write_check_ireason(drive, len, ireason))
+			return ide_stopped;
+
+		xferfunc = HWIF(drive)->atapi_output_bytes;
+	} else  {
+		/*
+		 * read from drive
+		 */
+		if (cdrom_read_check_ireason(drive, len, ireason))
+			return ide_stopped;
+
+		xferfunc = HWIF(drive)->atapi_input_bytes;
+	}
+
+	/*
+	 * transfer data
+	 */
+	while (thislen > 0) {
+		int blen = blen = rq->data_len;
+		char *ptr = rq->data;
+
+		/*
+		 * bio backed?
+		 */
+		if (rq->bio) {
+			ptr = bio_data(rq->bio);
+			blen = bio_iovec(rq->bio)->bv_len;
+		}
+
+		if (!ptr) {
+			printk(KERN_ERR "%s: confused, missing data\n", drive->name);
+			break;
+		}
+
+		if (blen > thislen)
+			blen = thislen;
+
+		xferfunc(drive, ptr, blen);
+
+		thislen -= blen;
+		len -= blen;
+		rq->data_len -= blen;
+
+		if (rq->bio)
+			end_that_request_chunk(rq, 1, blen);
+		else
+			rq->data += blen;
+	}
+
+	/*
+	 * pad, if necessary
+	 */
+	if (len > 0) {
+		while (len > 0) {
+			int pad = 0;
+
+			xferfunc(drive, &pad, sizeof(pad));
+			len -= sizeof(pad);
+		}
+	}
+
+	if (HWGROUP(drive)->handler != NULL)
+		BUG();
+
+	ide_set_handler(drive, cdrom_newpc_intr, rq->timeout, NULL);
+	return ide_started;
+
+end_request:
+	if (!rq->data_len)
+		post_transform_command(rq);
+
+	spin_lock_irqsave(&ide_lock, flags);
+	blkdev_dequeue_request(rq);
+	end_that_request_last(rq);
+	HWGROUP(drive)->rq = NULL;
+	spin_unlock_irqrestore(&ide_lock, flags);
+	return ide_stopped;
+}
+
+static ide_startstop_t cdrom_write_intr(ide_drive_t *drive)
+{
+	int stat, ireason, len, sectors_to_transfer, uptodate;
+	struct cdrom_info *info = drive->driver_data;
+	int dma_error = 0, dma = info->dma;
+	u8 lowcyl = 0, highcyl = 0;
+
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/* Check for errors. */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->ide_dma_end(drive))) {
+			printk(KERN_ERR "ide-cd: write dma error\n");
+			__ide_dma_off(drive);
+		}
+	}
+
+	if (cdrom_decode_status(drive, 0, &stat))
+		return ide_stopped;
+
+	/*
+	 * using dma, transfer is complete now
+	 */
+	if (dma) {
+		if (dma_error)
+			return ide_error(drive, "dma error", stat);
+
+		ide_end_request(drive, 1, rq->nr_sectors);
+		return ide_stopped;
+	}
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = HWIF(drive)->INB(IDE_IREASON_REG);
+	lowcyl  = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+	highcyl = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+
+	len = lowcyl + (256 * highcyl);
+
+	/* If DRQ is clear, the command has completed. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* If we're not done writing, complain.
+		 * Otherwise, complete the command normally.
+		 */
+		uptodate = 1;
+		if (rq->current_nr_sectors > 0) {
+			printk(KERN_ERR "%s: write_intr: data underrun (%d blocks)\n",
+			drive->name, rq->current_nr_sectors);
+			uptodate = 0;
+		}
+		cdrom_end_request(drive, uptodate);
+		return ide_stopped;
+	}
+
+	/* Check that the drive is expecting to do the same thing we are. */
+	if (cdrom_write_check_ireason(drive, len, ireason))
+		return ide_stopped;
+
+	sectors_to_transfer = len / SECTOR_SIZE;
+
+	/*
+	 * now loop and write out the data
+	 */
+	while (sectors_to_transfer > 0) {
+		int this_transfer;
+
+		if (!rq->current_nr_sectors) {
+			printk(KERN_ERR "ide-cd: write_intr: oops\n");
+			break;
+		}
+
+		/*
+		 * Figure out how many sectors we can transfer
+		 */
+		this_transfer = min_t(int, sectors_to_transfer, rq->current_nr_sectors);
+
+		while (this_transfer > 0) {
+			HWIF(drive)->atapi_output_bytes(drive, rq->buffer, SECTOR_SIZE);
+			rq->buffer += SECTOR_SIZE;
+			--rq->nr_sectors;
+			--rq->current_nr_sectors;
+			++rq->sector;
+			--this_transfer;
+			--sectors_to_transfer;
+		}
+
+		/*
+		 * current buffer complete, move on
+		 */
+		if (rq->current_nr_sectors == 0 && rq->nr_sectors)
+			cdrom_end_request(drive, 1);
+	}
+
+	/* re-arm handler */
+	ide_set_handler(drive, &cdrom_write_intr, ATAPI_WAIT_PC, NULL);
+	return ide_started;
+}
+
+static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+#if 0	/* the immediate bit */
+	rq->cmd[1] = 1 << 3;
+#endif
+	rq->timeout = ATAPI_WAIT_PC;
+
+	return cdrom_transfer_packet_command(drive, rq, cdrom_write_intr);
+}
+
+static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct gendisk *g = info->disk;
+	unsigned short sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+
+	/*
+	 * writes *must* be hardware frame aligned
+	 */
+	if ((rq->nr_sectors & (sectors_per_frame - 1)) ||
+	    (rq->sector & (sectors_per_frame - 1))) {
+		cdrom_end_request(drive, 0);
+		return ide_stopped;
+	}
+
+	/*
+	 * disk has become write protected
+	 */
+	if (g->policy) {
+		cdrom_end_request(drive, 0);
+		return ide_stopped;
+	}
+
+	/*
+	 * for dvd-ram and such media, it's a really big deal to get
+	 * big writes all the time. so scour the queue and attempt to
+	 * remerge requests, often the plugging will not have had time
+	 * to do this properly
+	 */
+	blk_attempt_remerge(drive->queue, rq);
+
+	info->nsectors_buffered = 0;
+
+	/* use dma, if possible. we don't need to check more, since we
+	 * know that the transfer is always (at least!) frame aligned */
+	info->dma = drive->using_dma ? 1 : 0;
+	info->cmd = WRITE;
+
+	info->devinfo.media_written = 1;
+
+	/* Start sending the write request to the drive. */
+	return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont);
+}
+
+static ide_startstop_t cdrom_do_newpc_cont(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+	if (!rq->timeout)
+		rq->timeout = ATAPI_WAIT_PC;
+
+	return cdrom_transfer_packet_command(drive, rq, cdrom_newpc_intr);
+}
+
+static ide_startstop_t cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	rq->flags |= REQ_QUIET;
+
+	info->dma = 0;
+	info->cmd = 0;
+
+	/*
+	 * sg request
+	 */
+	if (rq->bio) {
+		int mask = drive->queue->dma_alignment;
+		unsigned long addr = (unsigned long) page_address(bio_page(rq->bio));
+
+		info->cmd = rq_data_dir(rq);
+		info->dma = drive->using_dma;
+
+		/*
+		 * check if dma is safe
+		 */
+		if ((rq->data_len & mask) || (addr & mask))
+			info->dma = 0;
+	}
+
+	/* Start sending the command to the drive. */
+	return cdrom_start_packet_command(drive, rq->data_len, cdrom_do_newpc_cont);
+}
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+static ide_startstop_t
+ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block)
+{
+	ide_startstop_t action;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (blk_fs_request(rq)) {
+		if (CDROM_CONFIG_FLAGS(drive)->seeking) {
+			unsigned long elapsed = jiffies - info->start_seek;
+			int stat = HWIF(drive)->INB(IDE_STATUS_REG);
+
+			if ((stat & SEEK_STAT) != SEEK_STAT) {
+				if (elapsed < IDECD_SEEK_TIMEOUT) {
+					ide_stall_queue(drive, IDECD_SEEK_TIMER);
+					return ide_stopped;
+				}
+				printk (KERN_ERR "%s: DSC timeout\n", drive->name);
+			}
+			CDROM_CONFIG_FLAGS(drive)->seeking = 0;
+		}
+		if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
+			action = cdrom_start_seek(drive, block);
+		} else {
+			if (rq_data_dir(rq) == READ)
+				action = cdrom_start_read(drive, block);
+			else
+				action = cdrom_start_write(drive, rq);
+		}
+		info->last_block = block;
+		return action;
+	} else if (rq->flags & (REQ_PC | REQ_SENSE)) {
+		return cdrom_do_packet_command(drive);
+	} else if (rq->flags & REQ_BLOCK_PC) {
+		return cdrom_do_block_pc(drive, rq);
+	} else if (rq->flags & REQ_SPECIAL) {
+		/*
+		 * right now this can only be a reset...
+		 */
+		cdrom_end_request(drive, 1);
+		return ide_stopped;
+	}
+
+	blk_dump_rq_flags(rq, "ide-cd bad flags");
+	cdrom_end_request(drive, 0);
+	return ide_stopped;
+}
+
+
+
+/****************************************************************************
+ * Ioctl handling.
+ *
+ * Routines which queue packet commands take as a final argument a pointer
+ * to a request_sense struct.  If execution of the command results
+ * in an error with a CHECK CONDITION status, this structure will be filled
+ * with the results of the subsequent request sense command.  The pointer
+ * can also be NULL, in which case no sense information is returned.
+ */
+
+#if ! STANDARD_ATAPI
+static inline
+int bin2bcd (int x)
+{
+	return (x%10) | ((x/10) << 4);
+}
+
+
+static inline
+int bcd2bin (int x)
+{
+	return (x >> 4) * 10 + (x & 0x0f);
+}
+
+static
+void msf_from_bcd (struct atapi_msf *msf)
+{
+	msf->minute = bcd2bin (msf->minute);
+	msf->second = bcd2bin (msf->second);
+	msf->frame  = bcd2bin (msf->frame);
+}
+
+#endif /* not STANDARD_ATAPI */
+
+
+static inline
+void lba_to_msf (int lba, byte *m, byte *s, byte *f)
+{
+	lba += CD_MSF_OFFSET;
+	lba &= 0xffffff;  /* negative lbas use only 24 bits */
+	*m = lba / (CD_SECS * CD_FRAMES);
+	lba %= (CD_SECS * CD_FRAMES);
+	*s = lba / CD_FRAMES;
+	*f = lba % CD_FRAMES;
+}
+
+
+static inline
+int msf_to_lba (byte m, byte s, byte f)
+{
+	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
+}
+
+static int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense)
+{
+	struct request req;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.cmd[0] = GPCMD_TEST_UNIT_READY;
+	req.flags |= REQ_QUIET;
+
+#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   */
+
+	req.cmd[7] = cdi->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+static int
+cdrom_lockdoor(ide_drive_t *drive, int lockflag, struct request_sense *sense)
+{
+	struct request_sense my_sense;
+	struct request req;
+	int stat;
+
+	if (sense == NULL)
+		sense = &my_sense;
+
+	/* If the drive cannot lock the door, just pretend. */
+	if (CDROM_CONFIG_FLAGS(drive)->no_doorlock) {
+		stat = 0;
+	} else {
+		cdrom_prepare_request(drive, &req);
+		req.sense = sense;
+		req.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+		req.cmd[4] = lockflag ? 1 : 0;
+		stat = cdrom_queue_packet_command(drive, &req);
+	}
+
+	/* If we got an illegal field error, the drive
+	   probably cannot lock the door. */
+	if (stat != 0 &&
+	    sense->sense_key == ILLEGAL_REQUEST &&
+	    (sense->asc == 0x24 || sense->asc == 0x20)) {
+		printk (KERN_ERR "%s: door locking not supported\n",
+			drive->name);
+		CDROM_CONFIG_FLAGS(drive)->no_doorlock = 1;
+		stat = 0;
+	}
+	
+	/* no medium, that's alright. */
+	if (stat != 0 && sense->sense_key == NOT_READY && sense->asc == 0x3a)
+		stat = 0;
+
+	if (stat == 0)
+		CDROM_STATE_FLAGS(drive)->door_locked = lockflag;
+
+	return stat;
+}
+
+
+/* Eject the disk if EJECTFLAG is 0.
+   If EJECTFLAG is 1, try to reload the disk. */
+static int cdrom_eject(ide_drive_t *drive, int ejectflag,
+		       struct request_sense *sense)
+{
+	struct request req;
+	char loej = 0x02;
+
+	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;
+
+	cdrom_prepare_request(drive, &req);
+
+	/* only tell drive to close tray if open, if it can do that */
+	if (ejectflag && !CDROM_CONFIG_FLAGS(drive)->close_tray)
+		loej = 0;
+
+	req.sense = sense;
+	req.cmd[0] = GPCMD_START_STOP_UNIT;
+	req.cmd[4] = loej | (ejectflag != 0);
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
+			       unsigned long *sectors_per_frame,
+			       struct request_sense *sense)
+{
+	struct {
+		__u32 lba;
+		__u32 blocklen;
+	} capbuf;
+
+	int stat;
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.cmd[0] = GPCMD_READ_CDVD_CAPACITY;
+	req.data = (char *)&capbuf;
+	req.data_len = sizeof(capbuf);
+
+	stat = cdrom_queue_packet_command(drive, &req);
+	if (stat == 0) {
+		*capacity = 1 + be32_to_cpu(capbuf.lba);
+		*sectors_per_frame =
+			be32_to_cpu(capbuf.blocklen) >> SECTOR_BITS;
+	}
+
+	return stat;
+}
+
+static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
+				int format, char *buf, int buflen,
+				struct request_sense *sense)
+{
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.data =  buf;
+	req.data_len = buflen;
+	req.flags |= REQ_QUIET;
+	req.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+	req.cmd[6] = trackno;
+	req.cmd[7] = (buflen >> 8);
+	req.cmd[8] = (buflen & 0xff);
+	req.cmd[9] = (format << 6);
+
+	if (msf_flag)
+		req.cmd[1] = 2;
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense)
+{
+	int stat, ntracks, i;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	struct atapi_toc *toc = info->toc;
+	struct {
+		struct atapi_toc_header hdr;
+		struct atapi_toc_entry  ent;
+	} ms_tmp;
+	long last_written;
+	unsigned long sectors_per_frame = SECTORS_PER_FRAME;
+
+	if (toc == NULL) {
+		/* Try to allocate space. */
+		toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+						    GFP_KERNEL);
+		info->toc = toc;
+		if (toc == NULL) {
+			printk (KERN_ERR "%s: No cdrom TOC buffer!\n", drive->name);
+			return -ENOMEM;
+		}
+	}
+
+	/* Check to see if the existing data is still valid.
+	   If it is, just return. */
+	(void) cdrom_check_status(drive, sense);
+
+	if (CDROM_STATE_FLAGS(drive)->toc_valid)
+		return 0;
+
+	/* Try to get the total cdrom capacity and sector size. */
+	stat = cdrom_read_capacity(drive, &toc->capacity, &sectors_per_frame,
+				   sense);
+	if (stat)
+		toc->capacity = 0x1fffff;
+
+	set_capacity(info->disk, toc->capacity * sectors_per_frame);
+	blk_queue_hardsect_size(drive->queue,
+				sectors_per_frame << SECTOR_BITS);
+
+	/* 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), sense);
+	if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin(toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin(toc->hdr.last_track);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (ntracks <= 0)
+		return -EIO;
+	if (ntracks > MAX_TRACKS)
+		ntracks = MAX_TRACKS;
+
+	/* Now read the whole schmeer. */
+	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), sense);
+
+	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),
+					   sense);
+		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;
+		}
+	}
+
+	if (stat)
+		return stat;
+
+	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin(toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin(toc->hdr.last_track);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	for (i=0; i<=ntracks; i++) {
+#if ! STANDARD_ATAPI
+		if (CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd) {
+			if (CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd)
+				toc->ent[i].track = bcd2bin(toc->ent[i].track);
+			msf_from_bcd(&toc->ent[i].addr.msf);
+		}
+#endif  /* not STANDARD_ATAPI */
+		toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute,
+						   toc->ent[i].addr.msf.second,
+						   toc->ent[i].addr.msf.frame);
+	}
+
+	/* Read the multisession information. */
+	if (toc->hdr.first_track != CDROM_LEADOUT) {
+		/* Read the multisession information. */
+		stat = cdrom_read_tocentry(drive, 0, 0, 1, (char *)&ms_tmp,
+					   sizeof(ms_tmp), sense);
+		if (stat) return stat;
+
+		toc->last_session_lba = be32_to_cpu(ms_tmp.ent.addr.lba);
+	} else {
+		ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT;
+		toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */
+	}
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd) {
+		/* Re-read multisession information using MSF format */
+		stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp,
+					   sizeof(ms_tmp), sense);
+		if (stat)
+			return stat;
+
+		msf_from_bcd (&ms_tmp.ent.addr.msf);
+		toc->last_session_lba = msf_to_lba(ms_tmp.ent.addr.msf.minute,
+					  	   ms_tmp.ent.addr.msf.second,
+						   ms_tmp.ent.addr.msf.frame);
+	}
+#endif  /* not STANDARD_ATAPI */
+
+	toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+	/* Now try to get the total cdrom capacity. */
+	stat = cdrom_get_last_written(cdi, &last_written);
+	if (!stat && (last_written > toc->capacity)) {
+		toc->capacity = last_written;
+		set_capacity(info->disk, toc->capacity * sectors_per_frame);
+	}
+
+	/* Remember that we've read this stuff. */
+	CDROM_STATE_FLAGS(drive)->toc_valid = 1;
+
+	return 0;
+}
+
+
+static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf,
+				 int buflen, struct request_sense *sense)
+{
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	req.data = buf;
+	req.data_len = buflen;
+	req.cmd[0] = GPCMD_READ_SUBCHANNEL;
+	req.cmd[1] = 2;     /* MSF addressing */
+	req.cmd[2] = 0x40;  /* request subQ data */
+	req.cmd[3] = format;
+	req.cmd[7] = (buflen >> 8);
+	req.cmd[8] = (buflen & 0xff);
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+/* 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 cdrom_select_speed(ide_drive_t *drive, int speed,
+			      struct request_sense *sense)
+{
+	struct request req;
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = sense;
+	if (speed == 0)
+		speed = 0xffff; /* set to max */
+	else
+		speed *= 177;   /* Nx to kbytes/s */
+
+	req.cmd[0] = GPCMD_SET_SPEED;
+	/* Read Drive speed in kbytes/second MSB */
+	req.cmd[2] = (speed >> 8) & 0xff;	
+	/* Read Drive speed in kbytes/second LSB */
+	req.cmd[3] = speed & 0xff;
+	if (CDROM_CONFIG_FLAGS(drive)->cd_r ||
+	    CDROM_CONFIG_FLAGS(drive)->cd_rw ||
+	    CDROM_CONFIG_FLAGS(drive)->dvd_r) {
+		/* Write Drive speed in kbytes/second MSB */
+		req.cmd[4] = (speed >> 8) & 0xff;
+		/* Write Drive speed in kbytes/second LSB */
+		req.cmd[5] = speed & 0xff;
+       }
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end)
+{
+	struct request_sense sense;
+	struct request req;
+
+	cdrom_prepare_request(drive, &req);
+
+	req.sense = &sense;
+	req.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+	lba_to_msf(lba_start, &req.cmd[3], &req.cmd[4], &req.cmd[5]);
+	lba_to_msf(lba_end-1, &req.cmd[6], &req.cmd[7], &req.cmd[8]);
+
+	return cdrom_queue_packet_command(drive, &req);
+}
+
+static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
+				struct atapi_toc_entry **ent)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct atapi_toc *toc = info->toc;
+	int ntracks;
+
+	/*
+	 * don't serve cached data, if the toc isn't valid
+	 */
+	if (!CDROM_STATE_FLAGS(drive)->toc_valid)
+		return -EINVAL;
+
+	/* 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 ||
+		 track > toc->hdr.last_track)
+		return -EINVAL;
+	else
+		*ent = &toc->ent[track - toc->hdr.first_track];
+
+	return 0;
+}
+
+/* the generic packet interface to cdrom.c */
+static int ide_cdrom_packet(struct cdrom_device_info *cdi,
+			    struct packet_command *cgc)
+{
+	struct request req;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+	if (cgc->timeout <= 0)
+		cgc->timeout = ATAPI_WAIT_PC;
+
+	/* here we queue the commands from the uniform CD-ROM
+	   layer. the packet must be complete, as we do not
+	   touch it at all. */
+	cdrom_prepare_request(drive, &req);
+	memcpy(req.cmd, cgc->cmd, CDROM_PACKET_SIZE);
+	if (cgc->sense)
+		memset(cgc->sense, 0, sizeof(struct request_sense));
+	req.data = cgc->buffer;
+	req.data_len = cgc->buflen;
+	req.timeout = cgc->timeout;
+
+	if (cgc->quiet)
+		req.flags |= REQ_QUIET;
+
+	req.sense = cgc->sense;
+	cgc->stat = cdrom_queue_packet_command(drive, &req);
+	if (!cgc->stat)
+		cgc->buflen -= req.data_len;
+	return cgc->stat;
+}
+
+static
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct packet_command cgc;
+	char buffer[16];
+	int stat;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN);
+
+	/* These will be moved into the Uniform layer shortly... */
+	switch (cmd) {
+ 	case CDROMSETSPINDOWN: {
+ 		char spindown;
+ 
+ 		if (copy_from_user(&spindown, (void __user *) arg, sizeof(char)))
+			return -EFAULT;
+ 
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
+
+ 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+ 		return cdrom_mode_select(cdi, &cgc);
+ 	} 
+ 
+ 	case CDROMGETSPINDOWN: {
+ 		char spindown;
+ 
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
+ 
+ 		spindown = buffer[11] & 0x0f;
+ 
+		if (copy_to_user((void __user *) arg, &spindown, sizeof (char)))
+			return -EFAULT;
+ 
+ 		return 0;
+ 	}
+  
+	default:
+		return -EINVAL;
+	}
+
+}
+
+static
+int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
+			   unsigned int cmd, void *arg)
+			   
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+	int stat;
+
+	switch (cmd) {
+	/*
+	 * emulate PLAY_AUDIO_TI command with PLAY_AUDIO_10, since
+	 * atapi doesn't support it
+	 */
+	case CDROMPLAYTRKIND: {
+		unsigned long 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);
+		if (stat)
+			return stat;
+
+		stat = cdrom_get_toc_entry(drive, ti->cdti_trk1, &last_toc);
+		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_audio(drive, lba_start, lba_end);
+	}
+
+	case CDROMREADTOCHDR: {
+		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+		struct atapi_toc *toc;
+
+		/* Make sure our saved TOC is valid. */
+		stat = cdrom_read_toc(drive, NULL);
+		if (stat) return stat;
+
+		toc = info->toc;
+		tochdr->cdth_trk0 = toc->hdr.first_track;
+		tochdr->cdth_trk1 = toc->hdr.last_track;
+
+		return 0;
+	}
+
+	case CDROMREADTOCENTRY: {
+		struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
+		struct atapi_toc_entry *toce;
+
+		stat = cdrom_get_toc_entry(drive, tocentry->cdte_track, &toce);
+		if (stat) return stat;
+
+		tocentry->cdte_ctrl = toce->control;
+		tocentry->cdte_adr  = toce->adr;
+		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;
+	}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static
+int ide_cdrom_reset (struct cdrom_device_info *cdi)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct request_sense sense;
+	struct request req;
+	int ret;
+
+	cdrom_prepare_request(drive, &req);
+	req.flags = REQ_SPECIAL | REQ_QUIET;
+	ret = ide_do_drive_cmd(drive, &req, ide_wait);
+
+	/*
+	 * A reset will unlock the door. If it was previously locked,
+	 * lock it again.
+	 */
+	if (CDROM_STATE_FLAGS(drive)->door_locked)
+		(void) cdrom_lockdoor(drive, 1, &sense);
+
+	return ret;
+}
+
+
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct request_sense sense;
+
+	if (position) {
+		int stat = cdrom_lockdoor(drive, 0, &sense);
+		if (stat) return stat;
+	}
+
+	return cdrom_eject(drive, !position, &sense);
+}
+
+static
+int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	return cdrom_lockdoor(drive, lock, NULL);
+}
+
+static
+int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct request_sense sense;
+	int stat;
+
+	if ((stat = cdrom_select_speed(drive, speed, &sense)) < 0)
+		return stat;
+
+        cdi->speed = CDROM_STATE_FLAGS(drive)->current_speed;
+        return 0;
+}
+
+/*
+ * add logic to try GET_EVENT command first to check for media and tray
+ * status. this should be supported by newer cd-r/w and all DVD etc
+ * drives
+ */
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct media_event_desc med;
+	struct request_sense sense;
+	int stat;
+
+	if (slot_nr != CDSL_CURRENT)
+		return -EINVAL;
+
+	stat = cdrom_check_status(drive, &sense);
+	if (!stat || sense.sense_key == UNIT_ATTENTION)
+		return CDS_DISC_OK;
+
+	if (!cdrom_get_media_event(cdi, &med)) {
+		if (med.media_present)
+			return CDS_DISC_OK;
+		else if (med.door_open)
+			return CDS_TRAY_OPEN;
+		else
+			return CDS_NO_DISC;
+	}
+
+	if (sense.sense_key == NOT_READY && sense.asc == 0x04 && sense.ascq == 0x04)
+		return CDS_DISC_OK;
+
+	/*
+	 * If not using Mt Fuji extended media tray reports,
+	 * just return TRAY_OPEN since ATAPI doesn't provide
+	 * any other way to detect this...
+	 */
+	if (sense.sense_key == NOT_READY) {
+		if (sense.asc == 0x3a) {
+			if (sense.ascq == 1)
+				return CDS_NO_DISC;
+			else if (sense.ascq == 0 || sense.ascq == 2)
+				return CDS_TRAY_OPEN;
+		}
+	}
+
+	return CDS_DRIVE_NOT_READY;
+}
+
+static
+int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
+				struct cdrom_multisession *ms_info)
+{
+	struct atapi_toc *toc;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+	struct request_sense sense;
+	int ret;
+
+	if (!CDROM_STATE_FLAGS(drive)->toc_valid || info->toc == NULL)
+		if ((ret = cdrom_read_toc(drive, &sense)))
+			return ret;
+
+	toc = info->toc;
+	ms_info->addr.lba = toc->last_session_lba;
+	ms_info->xa_flag = toc->xa_flag;
+
+	return 0;
+}
+
+static
+int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
+		       struct cdrom_mcn *mcn_info)
+{
+	int stat;
+	char mcnbuf[24];
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+/* get MCN */
+	if ((stat = cdrom_read_subchannel(drive, 2, mcnbuf, sizeof (mcnbuf), NULL)))
+		return stat;
+
+	memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
+		sizeof (mcn_info->medium_catalog_number)-1);
+	mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
+		= '\0';
+
+	return 0;
+}
+
+
+
+/****************************************************************************
+ * Other driver requests (open, close, check media change).
+ */
+
+static
+int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
+				       int slot_nr)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	int retval;
+	
+	if (slot_nr == CDSL_CURRENT) {
+		(void) cdrom_check_status(drive, NULL);
+		retval = CDROM_STATE_FLAGS(drive)->media_changed;
+		CDROM_STATE_FLAGS(drive)->media_changed = 0;
+		return retval;
+	} else {
+		return -EINVAL;
+	}
+}
+
+
+static
+int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
+{
+	return 0;
+}
+
+/*
+ * Close down the device.  Invalidate all cached blocks.
+ */
+
+static
+void ide_cdrom_release_real (struct cdrom_device_info *cdi)
+{
+	ide_drive_t *drive = cdi->handle;
+
+	if (!cdi->use_count)
+		CDROM_STATE_FLAGS(drive)->toc_valid = 0;
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+static struct cdrom_device_ops ide_cdrom_dops = {
+	.open			= ide_cdrom_open_real,
+	.release		= ide_cdrom_release_real,
+	.drive_status		= ide_cdrom_drive_status,
+	.media_changed		= ide_cdrom_check_media_change_real,
+	.tray_move		= ide_cdrom_tray_move,
+	.lock_door		= ide_cdrom_lock_door,
+	.select_speed		= ide_cdrom_select_speed,
+	.get_last_session	= ide_cdrom_get_last_session,
+	.get_mcn		= ide_cdrom_get_mcn,
+	.reset			= ide_cdrom_reset,
+	.audio_ioctl		= ide_cdrom_audio_ioctl,
+	.dev_ioctl		= ide_cdrom_dev_ioctl,
+	.capability		= 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 | CDC_MO_DRIVE | CDC_MRW |
+				CDC_MRW_W | CDC_RAM,
+	.generic_packet		= ide_cdrom_packet,
+};
+
+static int ide_cdrom_register (ide_drive_t *drive, int nslots)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *devinfo = &info->devinfo;
+
+	devinfo->ops = &ide_cdrom_dops;
+	devinfo->mask = 0;
+	devinfo->speed = CDROM_STATE_FLAGS(drive)->current_speed;
+	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;
+	if (!CDROM_CONFIG_FLAGS(drive)->mo_drive)
+		devinfo->mask |= CDC_MO_DRIVE;
+
+	devinfo->disk = info->disk;
+	return register_cdrom(devinfo);
+}
+
+static
+int ide_cdrom_get_capabilities(ide_drive_t *drive, struct atapi_capabilities_page *cap)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	struct packet_command cgc;
+	int stat, attempts = 3, size = sizeof(*cap);
+
+	/*
+	 * ACER50 (and others?) require the full spec length mode sense
+	 * page capabilities size, but older drives break.
+	 */
+	if (!(!strcmp(drive->id->model, "ATAPI CD ROM DRIVE 50X MAX") ||
+	    !strcmp(drive->id->model, "WPI CDS-32X")))
+		size -= sizeof(cap->pad);
+
+	init_cdrom_command(&cgc, cap, size, CGC_DATA_UNKNOWN);
+	do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+		if (!stat)
+			break;
+	} while (--attempts);
+	return stat;
+}
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	struct atapi_capabilities_page cap;
+	int nslots = 1;
+
+	if (drive->media == ide_optical) {
+		CDROM_CONFIG_FLAGS(drive)->mo_drive = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+		printk(KERN_ERR "%s: ATAPI magneto-optical drive\n", drive->name);
+		return nslots;
+	}
+
+	if (CDROM_CONFIG_FLAGS(drive)->nec260 ||
+	    !strcmp(drive->id->model,"STINGRAY 8422 IDE 8X CD-ROM 7-27-95")) {
+		CDROM_CONFIG_FLAGS(drive)->no_eject = 0;
+		CDROM_CONFIG_FLAGS(drive)->audio_play = 1;
+		return nslots;
+	}
+
+	/*
+	 * 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 for cdi->ops.
+	 */
+	cdi->handle = (ide_drive_t *) drive;
+	cdi->ops = &ide_cdrom_dops;
+
+	if (ide_cdrom_get_capabilities(drive, &cap))
+		return 0;
+
+	if (cap.lock == 0)
+		CDROM_CONFIG_FLAGS(drive)->no_doorlock = 1;
+	if (cap.eject)
+		CDROM_CONFIG_FLAGS(drive)->no_eject = 0;
+	if (cap.cd_r_write)
+		CDROM_CONFIG_FLAGS(drive)->cd_r = 1;
+	if (cap.cd_rw_write) {
+		CDROM_CONFIG_FLAGS(drive)->cd_rw = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+	}
+	if (cap.test_write)
+		CDROM_CONFIG_FLAGS(drive)->test_write = 1;
+	if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom)
+		CDROM_CONFIG_FLAGS(drive)->dvd = 1;
+	if (cap.dvd_ram_write) {
+		CDROM_CONFIG_FLAGS(drive)->dvd_ram = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+	}
+	if (cap.dvd_r_write)
+		CDROM_CONFIG_FLAGS(drive)->dvd_r = 1;
+	if (cap.audio_play)
+		CDROM_CONFIG_FLAGS(drive)->audio_play = 1;
+	if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup)
+		CDROM_CONFIG_FLAGS(drive)->close_tray = 0;
+
+	/* Some drives used by Apple don't advertise audio play
+	 * but they do support reading TOC & audio datas
+	 */
+	if (strcmp(drive->id->model, "MATSHITADVD-ROM SR-8187") == 0 ||
+	    strcmp(drive->id->model, "MATSHITADVD-ROM SR-8186") == 0 ||
+	    strcmp(drive->id->model, "MATSHITADVD-ROM SR-8176") == 0 ||
+	    strcmp(drive->id->model, "MATSHITADVD-ROM SR-8174") == 0)
+		CDROM_CONFIG_FLAGS(drive)->audio_play = 1;
+
+#if ! STANDARD_ATAPI
+	if (cdi->sanyo_slot > 0) {
+		CDROM_CONFIG_FLAGS(drive)->is_changer = 1;
+		nslots = 3;
+	}
+
+	else
+#endif /* not STANDARD_ATAPI */
+	if (cap.mechtype == mechtype_individual_changer ||
+	    cap.mechtype == mechtype_cartridge_changer) {
+		if ((nslots = cdrom_number_of_slots(cdi)) > 1) {
+			CDROM_CONFIG_FLAGS(drive)->is_changer = 1;
+			CDROM_CONFIG_FLAGS(drive)->supp_disc_present = 1;
+		}
+	}
+
+	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+	if (!drive->id->model[0] &&
+	    !strncmp(drive->id->fw_rev, "241N", 4)) {
+		CDROM_STATE_FLAGS(drive)->current_speed  = 
+			(((unsigned int)cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS(drive)->max_speed = 
+			(((unsigned int)cap.maxspeed) + (176/2)) / 176;
+	} else {
+		CDROM_STATE_FLAGS(drive)->current_speed  = 
+			(ntohs(cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS(drive)->max_speed = 
+			(ntohs(cap.maxspeed) + (176/2)) / 176;
+	}
+
+	/* don't print speed if the drive reported 0.
+	 */
+	printk(KERN_INFO "%s: ATAPI", drive->name);
+	if (CDROM_CONFIG_FLAGS(drive)->max_speed)
+		printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed);
+	printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM");
+
+	if (CDROM_CONFIG_FLAGS(drive)->dvd_r|CDROM_CONFIG_FLAGS(drive)->dvd_ram)
+        	printk(" DVD%s%s", 
+        	(CDROM_CONFIG_FLAGS(drive)->dvd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS(drive)->dvd_ram)? "-RAM" : "");
+
+        if (CDROM_CONFIG_FLAGS(drive)->cd_r|CDROM_CONFIG_FLAGS(drive)->cd_rw) 
+        	printk(" CD%s%s", 
+        	(CDROM_CONFIG_FLAGS(drive)->cd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS(drive)->cd_rw)? "/RW" : "");
+
+        if (CDROM_CONFIG_FLAGS(drive)->is_changer) 
+        	printk(" changer w/%d slots", nslots);
+        else 	
+        	printk(" drive");
+
+	printk(", %dkB Cache", be16_to_cpu(cap.buffer_size));
+
+	if (drive->using_dma)
+		ide_dma_verbose(drive);
+
+	printk("\n");
+
+	return nslots;
+}
+
+static void ide_cdrom_add_settings(ide_drive_t *drive)
+{
+	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
+}
+
+/*
+ * standard prep_rq_fn that builds 10 byte cmds
+ */
+static int ide_cdrom_prep_fs(request_queue_t *q, struct request *rq)
+{
+	int hard_sect = queue_hardsect_size(q);
+	long block = (long)rq->hard_sector / (hard_sect >> 9);
+	unsigned long blocks = rq->hard_nr_sectors / (hard_sect >> 9);
+
+	memset(rq->cmd, 0, sizeof(rq->cmd));
+
+	if (rq_data_dir(rq) == READ)
+		rq->cmd[0] = GPCMD_READ_10;
+	else
+		rq->cmd[0] = GPCMD_WRITE_10;
+
+	/*
+	 * fill in lba
+	 */
+	rq->cmd[2] = (block >> 24) & 0xff;
+	rq->cmd[3] = (block >> 16) & 0xff;
+	rq->cmd[4] = (block >>  8) & 0xff;
+	rq->cmd[5] = block & 0xff;
+
+	/*
+	 * and transfer length
+	 */
+	rq->cmd[7] = (blocks >> 8) & 0xff;
+	rq->cmd[8] = blocks & 0xff;
+	rq->cmd_len = 10;
+	return BLKPREP_OK;
+}
+
+/*
+ * Most of the SCSI commands are supported directly by ATAPI devices.
+ * This transform handles the few exceptions.
+ */
+static int ide_cdrom_prep_pc(struct request *rq)
+{
+	u8 *c = rq->cmd;
+
+	/*
+	 * Transform 6-byte read/write commands to the 10-byte version
+	 */
+	if (c[0] == READ_6 || c[0] == WRITE_6) {
+		c[8] = c[4];
+		c[5] = c[3];
+		c[4] = c[2];
+		c[3] = c[1] & 0x1f;
+		c[2] = 0;
+		c[1] &= 0xe0;
+		c[0] += (READ_10 - READ_6);
+		rq->cmd_len = 10;
+		return BLKPREP_OK;
+	}
+
+	/*
+	 * it's silly to pretend we understand 6-byte sense commands, just
+	 * reject with ILLEGAL_REQUEST and the caller should take the
+	 * appropriate action
+	 */
+	if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) {
+		rq->errors = ILLEGAL_REQUEST;
+		return BLKPREP_KILL;
+	}
+	
+	return BLKPREP_OK;
+}
+
+static int ide_cdrom_prep_fn(request_queue_t *q, struct request *rq)
+{
+	if (rq->flags & REQ_CMD)
+		return ide_cdrom_prep_fs(q, rq);
+	else if (rq->flags & REQ_BLOCK_PC)
+		return ide_cdrom_prep_pc(rq);
+
+	return 0;
+}
+
+static
+int ide_cdrom_setup (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	int nslots;
+
+	blk_queue_prep_rq(drive->queue, ide_cdrom_prep_fn);
+	blk_queue_dma_alignment(drive->queue, 31);
+	drive->queue->unplug_delay = (1 * HZ) / 1000;
+	if (!drive->queue->unplug_delay)
+		drive->queue->unplug_delay = 1;
+
+	drive->special.all	= 0;
+
+	CDROM_STATE_FLAGS(drive)->media_changed = 1;
+	CDROM_STATE_FLAGS(drive)->toc_valid     = 0;
+	CDROM_STATE_FLAGS(drive)->door_locked   = 0;
+
+#if NO_DOOR_LOCKING
+	CDROM_CONFIG_FLAGS(drive)->no_doorlock = 1;
+#else
+	CDROM_CONFIG_FLAGS(drive)->no_doorlock = 0;
+#endif
+
+	CDROM_CONFIG_FLAGS(drive)->drq_interrupt = ((drive->id->config & 0x0060) == 0x20);
+	CDROM_CONFIG_FLAGS(drive)->is_changer = 0;
+	CDROM_CONFIG_FLAGS(drive)->cd_r = 0;
+	CDROM_CONFIG_FLAGS(drive)->cd_rw = 0;
+	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_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;
+	/* a testament to the nice quality of Samsung drives... */
+	if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430"))
+		CDROM_CONFIG_FLAGS(drive)->limit_nframes = 1;
+	else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432"))
+		CDROM_CONFIG_FLAGS(drive)->limit_nframes = 1;
+	/* the 3231 model does not support the SET_CD_SPEED command */
+	else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-3231"))
+		cdi->mask |= CDC_SELECT_SPEED;
+
+#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 */
+	cdi->sanyo_slot = 0;
+
+	CDROM_CONFIG_FLAGS(drive)->nec260 = 0;
+	CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd = 0;
+	CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd = 0;
+	CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 0;
+	CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 0;
+
+	if (strcmp (drive->id->model, "V003S0DS") == 0 &&
+	    drive->id->fw_rev[4] == '1' &&
+	    drive->id->fw_rev[6] <= '2') {
+		/* Vertos 300.
+		   Some versions of this drive like to talk BCD. */
+		CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 1;
+	}
+
+	else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
+	    drive->id->fw_rev[4] == '1' &&
+	    drive->id->fw_rev[6] <= '2') {
+		/* Vertos 600 ESD. */
+		CDROM_CONFIG_FLAGS(drive)->toctracks_as_bcd = 1;
+	}
+	else if (strcmp(drive->id->model, "NEC CD-ROM DRIVE:260") == 0 &&
+		 strncmp(drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */
+		/* Old NEC260 (not R).
+		   This drive was released before the 1.2 version
+		   of the spec. */
+		CDROM_CONFIG_FLAGS(drive)->tocaddr_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->nec260         = 1;
+	}
+	else if (strcmp(drive->id->model, "WEARNES CDD-120") == 0 &&
+		 strncmp(drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */
+		/* Wearnes */
+		CDROM_CONFIG_FLAGS(drive)->playmsf_as_bcd = 1;
+		CDROM_CONFIG_FLAGS(drive)->subchan_as_bcd = 1;
+	}
+        /* Sanyo 3 CD changer uses a non-standard command
+           for CD changing */
+        else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
+                 (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 */
+                 cdi->sanyo_slot = 3;
+        }
+#endif /* not STANDARD_ATAPI */
+
+	info->toc		= NULL;
+	info->buffer		= NULL;
+	info->sector_buffered	= 0;
+	info->nsectors_buffered	= 0;
+	info->changer_info      = NULL;
+	info->last_block	= 0;
+	info->start_seek	= 0;
+
+	nslots = ide_cdrom_probe_capabilities (drive);
+
+	/*
+	 * set correct block size
+	 */
+	blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE);
+
+	if (drive->autotune == IDE_TUNE_DEFAULT ||
+	    drive->autotune == IDE_TUNE_AUTO)
+		drive->dsc_overlap = (drive->next != drive);
+#if 0
+	drive->dsc_overlap = (HWIF(drive)->no_dsc) ? 0 : 1;
+	if (HWIF(drive)->no_dsc) {
+		printk(KERN_INFO "ide-cd: %s: disabling DSC overlap\n",
+			drive->name);
+		drive->dsc_overlap = 0;
+	}
+#endif
+
+	if (ide_cdrom_register(drive, nslots)) {
+		printk (KERN_ERR "%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name);
+		info->devinfo.handle = NULL;
+		return 1;
+	}
+	ide_cdrom_add_settings(drive);
+	return 0;
+}
+
+static
+sector_t ide_cdrom_capacity (ide_drive_t *drive)
+{
+	unsigned long capacity, sectors_per_frame;
+
+	if (cdrom_read_capacity(drive, &capacity, &sectors_per_frame, NULL))
+		return 0;
+
+	return capacity * sectors_per_frame;
+}
+
+static
+int ide_cdrom_cleanup(ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	if (ide_unregister_subdriver(drive)) {
+		printk(KERN_ERR "%s: %s: failed to ide_unregister_subdriver\n",
+			__FUNCTION__, drive->name);
+		return 1;
+	}
+
+	del_gendisk(info->disk);
+
+	ide_cd_put(info);
+
+	return 0;
+}
+
+static void ide_cd_release(struct kref *kref)
+{
+	struct cdrom_info *info = to_ide_cd(kref);
+	struct cdrom_device_info *devinfo = &info->devinfo;
+	ide_drive_t *drive = info->drive;
+	struct gendisk *g = info->disk;
+
+	if (info->buffer != NULL)
+		kfree(info->buffer);
+	if (info->toc != NULL)
+		kfree(info->toc);
+	if (info->changer_info != NULL)
+		kfree(info->changer_info);
+	if (devinfo->handle == drive && unregister_cdrom(devinfo))
+		printk(KERN_ERR "%s: %s failed to unregister device from the cdrom "
+				"driver.\n", __FUNCTION__, drive->name);
+	drive->dsc_overlap = 0;
+	drive->driver_data = NULL;
+	blk_queue_prep_rq(drive->queue, NULL);
+	g->private_data = NULL;
+	put_disk(g);
+	kfree(info);
+}
+
+static int ide_cdrom_attach (ide_drive_t *drive);
+
+#ifdef CONFIG_PROC_FS
+static int proc_idecd_read_capacity
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t*drive = (ide_drive_t *)data;
+	int len;
+
+	len = sprintf(page,"%llu\n", (long long)ide_cdrom_capacity(drive));
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t idecd_proc[] = {
+	{ "capacity", S_IFREG|S_IRUGO, proc_idecd_read_capacity, NULL },
+	{ NULL, 0, NULL, NULL }
+};
+#else
+# define idecd_proc	NULL
+#endif
+
+static ide_driver_t ide_cdrom_driver = {
+	.owner			= THIS_MODULE,
+	.name			= "ide-cdrom",
+	.version		= IDECD_VERSION,
+	.media			= ide_cdrom,
+	.busy			= 0,
+	.supports_dsc_overlap	= 1,
+	.cleanup		= ide_cdrom_cleanup,
+	.do_request		= ide_do_rw_cdrom,
+	.end_request		= ide_end_request,
+	.error			= __ide_error,
+	.abort			= __ide_abort,
+	.proc			= idecd_proc,
+	.attach			= ide_cdrom_attach,
+	.drives			= LIST_HEAD_INIT(ide_cdrom_driver.drives),
+};
+
+static int idecd_open(struct inode * inode, struct file * file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct cdrom_info *info;
+	ide_drive_t *drive;
+	int rc = -ENOMEM;
+
+	if (!(info = ide_cd_get(disk)))
+		return -ENXIO;
+
+	drive = info->drive;
+
+	drive->usage++;
+
+	if (!info->buffer)
+		info->buffer = kmalloc(SECTOR_BUFFER_SIZE,
+					GFP_KERNEL|__GFP_REPEAT);
+        if (!info->buffer || (rc = cdrom_open(&info->devinfo, inode, file)))
+		drive->usage--;
+
+	if (rc < 0)
+		ide_cd_put(info);
+
+	return rc;
+}
+
+static int idecd_release(struct inode * inode, struct file * file)
+{
+	struct gendisk *disk = inode->i_bdev->bd_disk;
+	struct cdrom_info *info = ide_cd_g(disk);
+	ide_drive_t *drive = info->drive;
+
+	cdrom_release (&info->devinfo, file);
+	drive->usage--;
+
+	ide_cd_put(info);
+
+	return 0;
+}
+
+static int idecd_ioctl (struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	struct block_device *bdev = inode->i_bdev;
+	struct cdrom_info *info = ide_cd_g(bdev->bd_disk);
+	int err;
+
+	err  = generic_ide_ioctl(info->drive, file, bdev, cmd, arg);
+	if (err == -EINVAL)
+		err = cdrom_ioctl(file, &info->devinfo, inode, cmd, arg);
+
+	return err;
+}
+
+static int idecd_media_changed(struct gendisk *disk)
+{
+	struct cdrom_info *info = ide_cd_g(disk);
+	return cdrom_media_changed(&info->devinfo);
+}
+
+static int idecd_revalidate_disk(struct gendisk *disk)
+{
+	struct cdrom_info *info = ide_cd_g(disk);
+	struct request_sense sense;
+	cdrom_read_toc(info->drive, &sense);
+	return  0;
+}
+
+static struct block_device_operations idecd_ops = {
+	.owner		= THIS_MODULE,
+	.open		= idecd_open,
+	.release	= idecd_release,
+	.ioctl		= idecd_ioctl,
+	.media_changed	= idecd_media_changed,
+	.revalidate_disk= idecd_revalidate_disk
+};
+
+/* options */
+static char *ignore = NULL;
+
+module_param(ignore, charp, 0400);
+MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
+
+static int ide_cdrom_attach (ide_drive_t *drive)
+{
+	struct cdrom_info *info;
+	struct gendisk *g;
+	struct request_sense sense;
+
+	if (!strstr("ide-cdrom", drive->driver_req))
+		goto failed;
+	if (!drive->present)
+		goto failed;
+	if (drive->media != ide_cdrom && drive->media != ide_optical)
+		goto failed;
+	/* skip drives that we were told to ignore */
+	if (ignore != NULL) {
+		if (strstr(ignore, drive->name)) {
+			printk(KERN_INFO "ide-cd: ignoring drive %s\n", drive->name);
+			goto failed;
+		}
+	}
+	if (drive->scsi) {
+		printk(KERN_INFO "ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name);
+		goto failed;
+	}
+	info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
+	if (info == NULL) {
+		printk(KERN_ERR "%s: Can't allocate a cdrom structure\n", drive->name);
+		goto failed;
+	}
+
+	g = alloc_disk(1 << PARTN_BITS);
+	if (!g)
+		goto out_free_cd;
+
+	ide_init_disk(g, drive);
+
+	if (ide_register_subdriver(drive, &ide_cdrom_driver)) {
+		printk(KERN_ERR "%s: Failed to register the driver with ide.c\n",
+			drive->name);
+		goto out_put_disk;
+	}
+	memset(info, 0, sizeof (struct cdrom_info));
+
+	kref_init(&info->kref);
+
+	info->drive = drive;
+	info->driver = &ide_cdrom_driver;
+	info->disk = g;
+
+	g->private_data = &info->driver;
+
+	drive->driver_data = info;
+
+	DRIVER(drive)->busy++;
+	g->minors = 1;
+	snprintf(g->devfs_name, sizeof(g->devfs_name),
+			"%s/cd", drive->devfs_name);
+	g->driverfs_dev = &drive->gendev;
+	g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE;
+	if (ide_cdrom_setup(drive)) {
+		struct cdrom_device_info *devinfo = &info->devinfo;
+		DRIVER(drive)->busy--;
+		ide_unregister_subdriver(drive);
+		if (info->buffer != NULL)
+			kfree(info->buffer);
+		if (info->toc != NULL)
+			kfree(info->toc);
+		if (info->changer_info != NULL)
+			kfree(info->changer_info);
+		if (devinfo->handle == drive && unregister_cdrom(devinfo))
+			printk (KERN_ERR "%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
+		kfree(info);
+		drive->driver_data = NULL;
+		goto failed;
+	}
+	DRIVER(drive)->busy--;
+
+	cdrom_read_toc(drive, &sense);
+	g->fops = &idecd_ops;
+	g->flags |= GENHD_FL_REMOVABLE;
+	add_disk(g);
+	return 0;
+
+out_put_disk:
+	put_disk(g);
+out_free_cd:
+	kfree(info);
+failed:
+	return 1;
+}
+
+static void __exit ide_cdrom_exit(void)
+{
+	ide_unregister_driver(&ide_cdrom_driver);
+}
+ 
+static int ide_cdrom_init(void)
+{
+	ide_register_driver(&ide_cdrom_driver);
+	return 0;
+}
+
+module_init(ide_cdrom_init);
+module_exit(ide_cdrom_exit);
+MODULE_LICENSE("GPL");