patch-2.3.99-pre1 linux/drivers/ide/ide-cd.c

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

diff -u --recursive --new-file v2.3.51/linux/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c
@@ -0,0 +1,2729 @@
+/*
+ * linux/drivers/block/ide-cd.c
+ * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
+ * Copyright (C) 1998, 1999 Jens Axboe <axboe@image.dk>
+ *
+ * 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/specs/INF-8020.PDF
+ * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r01.pdf
+ *
+ * Drives that deviate from these standards will be accomodated as much
+ * as possible via compile time or command-line options.  Since I only have
+ * a few drives, you generally need to send me patches...
+ *
+ * ----------------------------------
+ * 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 occured.
+ *			- Moved all the CDROMREADx ioctls to the Uniform layer.
+ *			- Heiko Eissfeldt <heiko@colossus.escape.de> supplied
+ *			  some fixes for CDI.
+ *			- CD-ROM leaving door locked fix from Andries
+ *			  Brouwer <Andries.Brouwer@cwi.nl>
+ *			- Erik Andersen <andersen@xmission.com> unified
+ *			  commands across the various drivers and how
+ *			  sense errors are handled.
+ *
+ * 4.56  Sep 12, 1999	- Removed changer support - it is now in the
+ *			  Uniform layer.
+ *			- Added partition based multisession handling.
+ *			- Mode sense and mode select moved to the
+ *			  Uniform layer.
+ *			- Fixed a problem with WPI CDS-32X drive - it
+ *			  failed the capabilities 
+ *
+ *
+ *************************************************************************/
+ 
+#define IDECD_VERSION "4.56"
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "ide-cd.h"
+
+/****************************************************************************
+ * 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
+void cdrom_analyze_sense_data (ide_drive_t *drive, struct request_sense *reqbuf,
+			       struct packet_command *failed_command)
+{
+	if (reqbuf->sense_key == NOT_READY ||
+	    reqbuf->sense_key == UNIT_ATTENTION) {
+		/* Make good and sure we've seen this potential media change.
+		   Some drives (i.e. Creative) fail to present the correct
+		   sense key in the error register. */
+		cdrom_saw_media_change (drive);
+
+
+		/* Don't print not ready or unit attention errors for
+		   READ_SUBCHANNEL.  Workman (and probably other programs)
+		   uses this command to poll the drive, and we don't want
+		   to fill the syslog with useless errors. */
+		if (failed_command &&
+		    failed_command->c[0] == GPCMD_READ_SUBCHANNEL)
+			return;
+	}
+
+	if (reqbuf->error_code == 0x70 && reqbuf->sense_key  == 0x02
+	 && ((reqbuf->asc      == 0x3a && reqbuf->ascq       == 0x00) ||
+	     (reqbuf->asc      == 0x04 && reqbuf->ascq       == 0x01)))
+	{
+		/*
+		 * Suppress the following errors:
+		 * "Medium not present", "in progress of becoming ready",
+		 * and "writing" to keep the noise level down to a dull roar.
+		 */
+		return;
+	}
+
+#if VERBOSE_IDE_CD_ERRORS
+	{
+		int i;
+		const char *s;
+		char buf[80];
+
+		printk ("ATAPI device %s:\n", drive->name);
+		if (reqbuf->error_code==0x70)
+			printk("  Error: ");
+		else if (reqbuf->error_code==0x71)
+			printk("  Deferred Error: ");
+		else
+			printk("  Unknown Error Type: ");
+
+		if ( reqbuf->sense_key < ARY_LEN (sense_key_texts))
+			s = sense_key_texts[reqbuf->sense_key];
+		else
+			s = "bad sense key!";
+
+		printk ("%s -- (Sense key=0x%02x)\n", s, reqbuf->sense_key);
+
+		if (reqbuf->asc == 0x40) {
+			sprintf (buf, "Diagnostic failure on component 0x%02x",
+				 reqbuf->ascq);
+			s = buf;
+		} else {
+			int lo=0, mid, hi=ARY_LEN (sense_data_texts);
+			unsigned long key = (reqbuf->sense_key << 16);
+			key |= (reqbuf->asc << 8);
+			if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+				key |= reqbuf->ascq;
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (sense_data_texts[mid].asc_ascq == key ||
+				    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 (reqbuf->asc > 0x80)
+				s = "(vendor-specific error)";
+			else
+				s = "(reserved error code)";
+		}
+
+		printk ("  %s -- (asc=0x%02x, ascq=0x%02x)\n",
+			s, reqbuf->asc, reqbuf->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->c[0]) {
+					s = packet_command_texts[mid].text;
+					break;
+				}
+				else if (packet_command_texts[mid].packet_command > failed_command->c[0])
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+
+			printk ("  The failed \"%s\" packet command was: \n  \"", s);
+			for (i=0; i<sizeof (failed_command->c); i++)
+				printk ("%02x ", failed_command->c[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 (reqbuf->sense_key == NOT_READY && (reqbuf->sks[0] & 0x80)) {
+			int progress = (reqbuf->sks[1] << 8 | reqbuf->sks[2]) * 100;
+			printk("  Command is %02d%% complete\n", progress / 0xffff);
+
+		}
+
+		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+		    (reqbuf->sks[0] & 0x80) != 0) {
+			printk ("  Error in %s byte %d",
+				(reqbuf->sks[0] & 0x40) != 0 ?
+				"command packet" : "command data",
+				(reqbuf->sks[1] << 8) + reqbuf->sks[2]);
+
+			if ((reqbuf->sks[0] & 0x40) != 0)
+				printk (" bit %d", reqbuf->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 (reqbuf->sense_key == UNIT_ATTENTION ||
+	    (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
+						reqbuf->asc == 0x3a)))
+		return;
+
+	printk ("%s: error code: 0x%02x  sense_key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
+		drive->name,
+		reqbuf->error_code, reqbuf->sense_key,
+		reqbuf->asc, reqbuf->ascq);
+#endif /* not VERBOSE_IDE_CD_ERRORS */
+}
+
+static void cdrom_queue_request_sense (ide_drive_t *drive, 
+				       struct semaphore *sem,
+				       struct packet_command *failed_command)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct request *rq;
+	struct packet_command *pc;
+
+	/* Make up a new request to retrieve sense information. */
+	pc = &info->request_sense_pc;
+	memset(pc, 0, sizeof (*pc));
+
+	pc->c[0] = GPCMD_REQUEST_SENSE;
+
+	/* just get the first 18 bytes of the sense info, there might not
+	 * be more available */
+	pc->c[4] = pc->buflen = 18;
+	pc->buffer = (char *)&info->sense_data;
+	pc->sense_data = (struct request_sense *)failed_command;
+
+	/* stuff the sense request in front of our current request */
+	rq = &info->request_sense_request;
+	ide_init_drive_cmd (rq);
+	rq->cmd = REQUEST_SENSE_COMMAND;
+	rq->buffer = (char *)pc;
+	rq->sem = sem;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+
+static void cdrom_end_request (int uptodate, ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+
+	if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
+		struct packet_command *pc = (struct packet_command *)
+			                      rq->buffer;
+		cdrom_analyze_sense_data (drive,
+			(struct request_sense *) (pc->buffer - pc->c[4]),
+			(struct packet_command *) pc->sense_data);
+	}
+	if (rq->cmd == READ && !rq->current_nr_sectors)
+		uptodate = 1;
+
+	ide_end_request (uptodate, HWGROUP(drive));
+}
+
+
+/* Returns 0 if the request should be continued.
+   Returns 1 if the request was ended. */
+static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, int good_stat,
+				int *stat_ret)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	int stat, cmd, err, sense_key;
+	struct packet_command *pc = (struct packet_command *) rq->buffer;
+	
+	/* Check for errors. */
+	stat = GET_STAT();
+	*stat_ret = stat;
+
+	if (OK_STAT (stat, good_stat, BAD_R_STAT))
+		return 0;
+
+	/* Get the IDE error register. */
+	err = GET_ERR();
+	sense_key = err >> 4;
+
+	if (rq == NULL)
+		printk ("%s: missing request in cdrom_decode_status\n",
+			drive->name);
+	else {
+		cmd = rq->cmd;
+
+		if (cmd == REQUEST_SENSE_COMMAND) {
+			/* We got an error trying to get sense info
+			   from the drive (probably while trying
+			   to recover from a former error).  Just give up. */
+
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+			*startstop = ide_error (drive, "request sense failure", stat);
+			return 1;
+
+		} else if (cmd == PACKET_COMMAND) {
+			/* All other functions, except for READ. */
+
+			struct semaphore *sem = NULL;
+
+			/* 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 {
+				/* Otherwise, print an error. */
+				ide_dump_status (drive, "packet command error",
+						 stat);
+			}
+			
+			/* Set the error flag and complete the request.
+			   Then, if we have a CHECK CONDITION status,
+			   queue a request sense command.  We must be careful,
+			   though: we don't want the thread in
+			   cdrom_queue_packet_command to wake up until
+			   the request sense has completed.  We do this
+			   by transferring the semaphore from the packet
+			   command request to the request sense request. */
+
+			if ((stat & ERR_STAT) != 0) {
+				sem = rq->sem;
+				rq->sem = NULL;
+			}
+
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+
+			if ((stat & ERR_STAT) != 0)
+				cdrom_queue_request_sense(drive, sem, pc);
+		} else {
+			/* Handle errors from READ requests. */
+
+			if (sense_key == NOT_READY) {
+				/* Tray open. */
+				cdrom_saw_media_change (drive);
+
+				/* Fail the request. */
+				printk ("%s: tray open\n", drive->name);
+				cdrom_end_request (0, drive);
+			} 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)
+					cdrom_end_request (0, drive);
+			} 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);
+				cdrom_end_request (0, drive);
+			} else if ((err & ~ABRT_ERR) != 0) {
+				/* Go to the default handler
+				   for other errors. */
+				*startstop = ide_error (drive, "cdrom_decode_status", stat);
+				return 1;
+			} else if ((++rq->errors > ERROR_MAX)) {
+				/* We've racked up too many retries.  Abort. */
+				cdrom_end_request (0, drive);
+			}
+
+			/* If we got a CHECK_CONDITION status,
+			   queue a request sense command. */
+			if ((stat & ERR_STAT) != 0)
+				cdrom_queue_request_sense(drive, NULL, NULL);
+		}
+	}
+
+	/* Retry, or handle the next request. */
+	*startstop = ide_stopped;
+	return 1;
+}
+
+static int cdrom_timer_expiry(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *) rq->buffer;
+	unsigned long wait = 0;
+
+	/* blank and format can take an extremly long time to
+	 * complete, if the IMMED bit was not set.
+	 */
+	if (pc->c[0] == GPCMD_BLANK || pc->c[0] == GPCMD_FORMAT_UNIT)
+		wait = 60*60*HZ;
+
+	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;
+
+	/* 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(drive)->dmaproc(ide_dma_read, drive);
+
+	/* Set up the controller registers. */
+	OUT_BYTE (info->dma, IDE_FEATURE_REG);
+	OUT_BYTE (0, IDE_NSECTOR_REG);
+	OUT_BYTE (0, IDE_SECTOR_REG);
+
+	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
+	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
+	if (IDE_CONTROL_REG)
+		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+ 
+	if (info->dma)
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+
+	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+		ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+		return ide_started;
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+		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. */
+static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
+                                          unsigned char *cmd_buf, int cmd_len,
+					  ide_handler_t *handler)
+{
+	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. */
+		int stat_dum;
+		ide_startstop_t startstop;
+
+		/* Check for errors. */
+		if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum))
+			return startstop;
+	} else {
+		ide_startstop_t startstop;
+		/* 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, WAIT_CMD, cdrom_timer_expiry);
+
+	/* Send the command to the device. */
+	atapi_output_bytes (drive, cmd_buf, cmd_len);
+
+	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 (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) {
+		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) {
+		char dum[SECTOR_SIZE];
+		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)
+{
+	ireason &= 3;
+	if (ireason == 2) return 0;
+
+	if (ireason == 0) {
+		/* Whoops... The drive is expecting to receive data from us! */
+		printk ("%s: cdrom_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;
+			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.
+		 */
+		GET_STAT();
+		return 0;
+	} else {
+		/* Drive wants a command packet, or invalid ireason... */
+		printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+			drive->name, ireason);
+	}
+
+	cdrom_end_request (0, drive);
+	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;
+	int i, dma = info->dma, dma_error = 0;
+	ide_startstop_t startstop;
+
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/* Check for errors. */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive)))
+			HWIF(drive)->dmaproc(ide_dma_off, drive);
+	}
+
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
+ 
+	if (dma) {
+		if (!dma_error) {
+			for (i = rq->nr_sectors; i > 0;) {
+				i -= rq->current_nr_sectors;
+				ide_end_request(1, HWGROUP(drive));
+			}
+			return ide_stopped;
+		} else
+			return ide_error (drive, "dma error", stat);
+	}
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = IN_BYTE (IDE_NSECTOR_REG);
+	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+	/* 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 ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request (0, drive);
+		} else
+			cdrom_end_request (1, drive);
+		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 ("%s: cdrom_read_intr: Bad transfer size %d\n",
+			drive->name, len);
+		if (CDROM_CONFIG_FLAGS (drive)->limit_nframes)
+			printk ("  This drive is not supported by this version of the driver\n");
+		else {
+			printk ("  Trying to limit transfer sizes\n");
+			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+		}
+		cdrom_end_request (0, drive);
+		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 ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
+		     sectors_to_transfer);
+
+	while (nskip > 0) {
+		/* We need to throw away a sector. */
+		char dum[SECTOR_SIZE];
+		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 > 0)
+			cdrom_end_request (1, drive);
+
+		/* 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 (sectors_to_transfer,
+					     rq->current_nr_sectors);
+
+			/* Read this_transfer sectors
+			   into the current buffer. */
+			while (this_transfer > 0) {
+				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, WAIT_CMD, 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;
+
+	/* 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 (1, drive);
+
+		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 (1, drive);
+		return -1;
+	}
+
+	/* Move on to the next buffer if needed. */
+	if (rq->current_nr_sectors == 0)
+		cdrom_end_request (1, drive);
+
+	/* 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 < (rq->bh->b_size >> SECTOR_BITS) &&
+	    (rq->sector % SECTORS_PER_FRAME) != 0) {
+		printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+			drive->name, rq->sector);
+		cdrom_end_request (0, drive);
+		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 packet_command pc;
+	struct request *rq = HWGROUP(drive)->rq;
+	int nsect, sector, nframes, frame, nskip;
+
+	/* Number of sectors to transfer. */
+	nsect = rq->nr_sectors;
+
+	/* Starting sector. */
+	sector = rq->sector;
+
+	/* 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 = (sector % SECTORS_PER_FRAME);
+	if (nskip > 0) {
+		/* Sanity check... */
+		if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS) &&
+			(rq->sector % CD_FRAMESIZE != 0)) {
+			printk ("%s: cdrom_start_read_continuation: buffer botch (%lu)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request (0, drive);
+			return ide_stopped;
+		}
+		sector -= nskip;
+		nsect += nskip;
+		rq->current_nr_sectors += nskip;
+	}
+
+	/* Convert from sectors to cdrom blocks, rounding up the transfer
+	   length if needed. */
+	nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+	frame = sector / SECTORS_PER_FRAME;
+
+	/* Largest number of frames was can transfer at once is 64k-1. For
+	   some drives we need to limit this even more. */
+	nframes = MIN (nframes, (CDROM_CONFIG_FLAGS (drive)->limit_nframes) ?
+		(65534 / CD_FRAMESIZE) : 65535);
+
+	/* Set up the command */
+	memset (&pc.c, 0, sizeof (pc.c));
+	pc.c[0] = GPCMD_READ_10;
+	pc.c[7] = (nframes >> 8);
+	pc.c[8] = (nframes & 0xff);
+	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
+					      &cdrom_read_intr);
+}
+
+
+#define IDECD_SEEK_THRESHOLD	(1000)			/* 1000 blocks */
+#define IDECD_SEEK_TIMER	(5 * WAIT_MIN_SLEEP)	/* 100 ms */
+#define IDECD_SEEK_TIMEOUT     WAIT_CMD			/* 10 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;
+	ide_startstop_t startstop;
+
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
+	CDROM_CONFIG_FLAGS(drive)->seeking = 1;
+
+	if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) {
+		if (--retry == 0) {
+			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 packet_command pc;
+	struct request *rq = HWGROUP(drive)->rq;
+	int sector, frame, nskip;
+
+	sector = rq->sector;
+	nskip = (sector % SECTORS_PER_FRAME);
+	if (nskip > 0)
+		sector -= nskip;
+	frame = sector / SECTORS_PER_FRAME;
+
+	memset (&pc.c, 0, sizeof (pc.c));
+	pc.c[0] = GPCMD_SEEK;
+	put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]);
+	return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &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->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 != rq->bh->b_data) {
+		int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+		rq->buffer = rq->bh->b_data;
+		rq->nr_sectors += n;
+		rq->sector -= n;
+	}
+	rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+}
+
+/*
+ * 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;
+	int minor = MINOR (rq->rq_dev);
+
+	/* If the request is relative to a partition, fix it up to refer to the
+	   absolute address.  */
+	if ((minor & PARTN_MASK) != 0) {
+		rq->sector = block;
+		minor &= ~PARTN_MASK;
+		rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
+	}
+
+	/* 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;
+
+	/* Clear the local sector buffer. */
+	info->nsectors_buffered = 0;
+
+	/* use dma, if possible. */
+	if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) &&
+				(rq->nr_sectors % SECTORS_PER_FRAME == 0))
+		info->dma = 1;
+	else
+		info->dma = 0;
+
+	/* Start sending the read request to the drive. */
+	return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation);
+}
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Forward declarations. */
+static int cdrom_lockdoor(ide_drive_t *drive, int lockflag);
+
+/* Interrupt routine for packet command completion. */
+static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
+{
+	int ireason, len, stat, thislen;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
+	struct cdrom_info *info = drive->driver_data;
+	ide_startstop_t startstop;
+
+	pc->sense_data = &info->sense_data;
+
+	/* Check for errors. */
+	if (cdrom_decode_status (&startstop, drive, 0, &stat))
+		return startstop;
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = IN_BYTE (IDE_NSECTOR_REG);
+	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+	/* 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 (pc->c[0] == GPCMD_REQUEST_SENSE &&
+		    pc->buflen > 0 &&
+		    pc->buflen <= 5) {
+			while (pc->buflen > 0) {
+				*pc->buffer++ = 0;
+				--pc->buflen;
+			}
+		}
+
+		if (pc->buflen == 0)
+			cdrom_end_request (1, drive);
+		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);
+			*/
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+		}
+		return ide_stopped;
+	}
+
+	/* Figure out how much data to transfer. */
+	thislen = pc->buflen;
+	if (thislen > len) thislen = len;
+
+	/* The drive wants to be written to. */
+	if ((ireason & 3) == 0) {
+		/* Transfer the data. */
+		atapi_output_bytes (drive, pc->buffer, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			atapi_output_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		pc->buffer += thislen;
+		pc->buflen -= thislen;
+	}
+
+	/* Same drill for reading. */
+	else if ((ireason & 3) == 2) {
+
+		/* Transfer the data. */
+		atapi_input_bytes (drive, pc->buffer, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			atapi_input_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		pc->buffer += thislen;
+		pc->buflen -= thislen;
+	} else {
+		printk ("%s: cdrom_pc_intr: The drive "
+			"appears confused (ireason = 0x%2x)\n",
+			drive->name, ireason);
+		pc->stat = 1;
+	}
+
+	/* Now we wait for another interrupt. */
+	ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD, cdrom_timer_expiry);
+	return ide_started;
+}
+
+
+static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+	/* Send the command to the drive and return. */
+	return cdrom_transfer_packet_command (drive, pc->c,
+				       sizeof (pc->c), &cdrom_pc_intr);
+}
+
+
+static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
+{
+	int len;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	pc->stat = 0;
+	len = pc->buflen;
+
+	/* Start sending the command to the drive. */
+	return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
+}
+
+
+/* Sleep for TIME jiffies.
+   Not to be called from an interrupt handler. */
+static
+void cdrom_sleep (int time)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(time);
+}
+
+static
+int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc)
+{
+	int retries = 10;
+	struct request req;
+
+	/* Start of retry loop. */
+	do {
+		ide_init_drive_cmd (&req);
+		req.cmd = PACKET_COMMAND;
+		req.buffer = (char *)pc;
+		if (ide_do_drive_cmd (drive, &req, ide_wait)) {
+			printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
+				drive->name, req.buffer[0], req.buffer[1]);
+			/* FIXME: we should probably abort/retry or something */
+		}
+		if (pc->stat != 0) {
+			/* The request failed.  Retry if it was due to a unit
+			   attention status
+			   (usually means media was changed). */
+			struct request_sense *reqbuf = pc->sense_data;
+
+			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. */
+				cdrom_sleep (HZ);
+			} else {
+				/* Otherwise, don't retry. */
+				retries = 0;
+			}
+			--retries;
+		}
+
+		/* End of retry loop. */
+	} while (pc->stat != 0 && retries >= 0);
+
+	/* Return an error if the command failed. */
+	if (pc->stat)
+		return -EIO;
+
+	/* The command succeeded.  If it was anything other than
+	   a request sense, eject, or door lock command,
+	   and we think that the door is presently unlocked, lock it
+	   again. (The door was probably unlocked via an explicit
+	   CDROMEJECT ioctl.) */
+	if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
+	    (pc->c[0] != GPCMD_TEST_UNIT_READY &&
+	     pc->c[0] != GPCMD_REQUEST_SENSE &&
+	     pc->c[0] != GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL &&
+	     pc->c[0] != GPCMD_START_STOP_UNIT &&
+	     pc->c[0] != GPCMD_MODE_SENSE_10 &&
+	     pc->c[0] != GPCMD_MODE_SELECT_10)) {
+		(void) cdrom_lockdoor (drive, 1);
+	}
+	return 0;
+}
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+static ide_startstop_t
+ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	ide_startstop_t action;
+	struct cdrom_info *info = drive->driver_data;
+
+	switch (rq->cmd) {
+		case READ: {
+			if (CDROM_CONFIG_FLAGS(drive)->seeking) {
+				unsigned long elpased = jiffies - info->start_seek;
+				int stat = GET_STAT();
+
+				if ((stat & SEEK_STAT) != SEEK_STAT) {
+					if (elpased < IDECD_SEEK_TIMEOUT) {
+						ide_stall_queue(drive, IDECD_SEEK_TIMER);
+						return ide_stopped;
+					}
+					printk ("%s: DSC timeout\n", drive->name);
+				}
+				CDROM_CONFIG_FLAGS(drive)->seeking = 0;
+			}
+			if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
+				action = cdrom_start_seek (drive, block);
+			else
+				action = cdrom_start_read (drive, block);
+			info->last_block = block;
+			return action;
+		}
+
+		case PACKET_COMMAND:
+		case REQUEST_SENSE_COMMAND: {
+			return cdrom_do_packet_command(drive);
+		}
+
+		case RESET_DRIVE_COMMAND: {
+			cdrom_end_request(1, drive);
+			return ide_do_reset(drive);
+		}
+
+		default: {
+			printk("ide-cd: bad cmd %d\n", rq -> cmd);
+			cdrom_end_request(0, drive);
+			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 packet_command pc;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+
+	memset(&pc, 0, sizeof(pc));
+
+	pc.c[0] = GPCMD_TEST_UNIT_READY;
+
+#if ! STANDARD_ATAPI
+        /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
+           switch CDs instead of supporting the LOAD_UNLOAD opcode   */
+
+        pc.c[7] = cdi->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
+
+	return cdrom_queue_packet_command(drive, &pc);
+}
+
+
+/* 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 packet_command pc;
+	int stat;
+
+	/* If the drive cannot lock the door, just pretend. */
+	if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
+		stat = 0;
+	else {
+		memset(&pc, 0, sizeof(pc));
+		pc.c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+		pc.c[4] = (lockflag != 0);
+		stat = cdrom_queue_packet_command (drive, &pc);
+	}
+
+	sense = pc.sense_data;
+
+	/* 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 ("%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 packet_command pc;
+
+	if (CDROM_CONFIG_FLAGS(drive)->no_eject && !ejectflag)
+		return -EDRIVE_CANT_DO_THIS;
+	
+	/* reload fails on some drives, if the tray is locked */
+	if (CDROM_STATE_FLAGS(drive)->door_locked && ejectflag)
+		return 0;
+
+	memset(&pc, 0, sizeof (pc));
+
+	pc.c[0] = GPCMD_START_STOP_UNIT;
+	pc.c[4] = 0x02 + (ejectflag != 0);
+	return cdrom_queue_packet_command (drive, &pc);
+}
+
+static int cdrom_read_capacity(ide_drive_t *drive, unsigned *capacity)
+{
+	struct {
+		__u32 lba;
+		__u32 blocklen;
+	} capbuf;
+
+	int stat;
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof (pc));
+
+	pc.c[0] = GPCMD_READ_CDVD_CAPACITY;
+	pc.buffer = (char *)&capbuf;
+	pc.buflen = sizeof(capbuf);
+
+	stat = cdrom_queue_packet_command(drive, &pc);
+	if (stat == 0)
+		*capacity = be32_to_cpu(capbuf.lba);
+
+	return stat;
+}
+
+static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
+				int format, char *buf, int buflen)
+{
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof(pc));
+
+	pc.buffer =  buf;
+	pc.buflen = buflen;
+	pc.c[0] = GPCMD_READ_TOC_PMA_ATIP;
+	pc.c[6] = trackno;
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	pc.c[9] = (format << 6);
+
+	if (msf_flag)
+		pc.c[1] = 2;
+
+	return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int cdrom_read_toc (ide_drive_t *drive)
+{
+	int stat, ntracks, i;
+	struct cdrom_info *info = drive->driver_data;
+	struct atapi_toc *toc = info->toc;
+	int minor = drive->select.b.unit << PARTN_BITS;
+	struct {
+		struct atapi_toc_header hdr;
+		struct atapi_toc_entry  ent;
+	} ms_tmp;
+
+	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 ("%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. */
+	if (CDROM_STATE_FLAGS (drive)->toc_valid)
+		(void) cdrom_check_status(drive);
+
+	if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
+
+	/* 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));
+	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));
+
+	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));
+		if (stat) {
+			return stat;
+		}
+#if ! STANDARD_ATAPI
+		if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+			toc->hdr.first_track = bin2bcd(CDROM_LEADOUT);
+			toc->hdr.last_track = bin2bcd(CDROM_LEADOUT);
+		} else
+#endif  /* not STANDARD_ATAPI */
+		{
+			toc->hdr.first_track = CDROM_LEADOUT;
+			toc->hdr.last_track = CDROM_LEADOUT;
+		}
+	} else if (stat) {
+		return stat;
+	}
+	if (stat) return stat;
+
+	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+	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, 1, 1,
+					   (char *)&ms_tmp, sizeof (ms_tmp));
+		if (stat) return stat;
+	} else {
+		ms_tmp.ent.addr.msf.minute = 0;
+		ms_tmp.ent.addr.msf.second = 2;
+		ms_tmp.ent.addr.msf.frame  = 0;
+		ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT;
+	}
+
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
+		msf_from_bcd (&ms_tmp.ent.addr.msf);
+#endif  /* not STANDARD_ATAPI */
+
+	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);
+
+	toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+	/* Now try to get the total cdrom capacity. */
+#if 0
+	stat = cdrom_get_last_written(MKDEV(HWIF(drive)->major, minor),
+				     (long *)&toc->capacity);
+	if (stat)
+#endif
+	stat = cdrom_read_capacity (drive, &toc->capacity);
+	if (stat) toc->capacity = 0x1fffff;
+
+	/* for general /dev/cdrom like mounting, one big disc */
+	drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+	HWIF(drive)->gd->sizes[minor] = (toc->capacity * SECTORS_PER_FRAME) >>
+					(BLOCK_SIZE_BITS - 9);
+
+	/* Remember that we've read this stuff. */
+	CDROM_STATE_FLAGS (drive)->toc_valid = 1;
+
+	/* should be "if multisession", but it does no harm. */
+	if (ntracks == 1)
+		return 0;
+
+	/* setup each minor to respond to a session */
+	minor++;
+	i = toc->hdr.first_track;
+	while ((i <= ntracks) && ((minor & CD_PART_MASK) < CD_PART_MAX)) {
+		drive->part[minor & PARTN_MASK].start_sect = 0;
+ 		drive->part[minor & PARTN_MASK].nr_sects =
+			(toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) << (BLOCK_SIZE_BITS - 9);
+		HWIF(drive)->gd->sizes[minor] = (toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
+		i++;
+		minor++;
+	}
+
+	return 0;
+}
+
+
+static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf,
+				 int buflen)
+{
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof(pc));
+
+	pc.buffer = buf;
+	pc.buflen = buflen;
+	pc.c[0] = GPCMD_READ_SUBCHANNEL;
+	pc.c[1] = 2;     /* MSF addressing */
+	pc.c[2] = 0x40;  /* request subQ data */
+	pc.c[3] = format;
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	return cdrom_queue_packet_command(drive, &pc);
+}
+
+/* ATAPI cdrom drives are free to select the speed you request or any slower
+   rate :-( Requesting too fast a speed will _not_ produce an error. */
+static int cdrom_select_speed (ide_drive_t *drive, int speed)
+{
+	struct packet_command pc;
+	memset(&pc, 0, sizeof(pc));
+
+	if (speed == 0)
+		speed = 0xffff; /* set to max */
+	else
+		speed *= 177;   /* Nx to kbytes/s */
+
+	pc.c[0] = GPCMD_SET_SPEED;
+	/* Read Drive speed in kbytes/second MSB */
+	pc.c[2] = (speed >> 8) & 0xff;	
+	/* Read Drive speed in kbytes/second LSB */
+	pc.c[3] = speed & 0xff;
+	if ( CDROM_CONFIG_FLAGS(drive)->cd_r ||
+                   CDROM_CONFIG_FLAGS(drive)->cd_rw ) {
+		/* Write Drive speed in kbytes/second MSB */
+		pc.c[4] = (speed >> 8) & 0xff;
+		/* Write Drive speed in kbytes/second LSB */
+		pc.c[5] = speed & 0xff;
+       }
+
+	return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+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;
+
+	/* 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 cdrom_generic_command *cgc)
+{
+	struct packet_command pc;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+	/* here we queue the commands from the uniform CD-ROM
+	   layer. the packet must be complete, as we do not
+	   touch it at all. */
+	memset(&pc, 0, sizeof(pc));
+	memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE);
+	pc.buffer = cgc->buffer;
+	pc.buflen = cgc->buflen;
+	cgc->stat = cdrom_queue_packet_command(drive, &pc);
+
+	/* There was an error, assign sense. */
+	if (cgc->stat)
+		cgc->sense = pc.sense_data;
+
+	return cgc->stat;
+}
+
+static
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct cdrom_generic_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 *) 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 *) 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;
+
+	switch (cmd) {
+	case CDROMREADTOCHDR: {
+		int stat;
+		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+		struct atapi_toc *toc;
+
+		/* Make sure our saved TOC is valid. */
+		stat = cdrom_read_toc(drive);
+		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: {
+		int stat;
+		struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
+		struct atapi_toc_entry *toce;
+
+		stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce);
+		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 req;
+
+	ide_init_drive_cmd (&req);
+	req.cmd = RESET_DRIVE_COMMAND;
+	return ide_do_drive_cmd (drive, &req, ide_wait);
+}
+
+
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+	if (position) {
+		int stat = cdrom_lockdoor (drive, 0);
+		if (stat) return stat;
+	}
+
+	return cdrom_eject(drive, !position);
+}
+
+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);
+}
+
+#undef __ACER50__
+
+#ifdef __ACER50__
+/*
+ * the buffer struct used by ide_cdrom_get_capabilities()
+ */
+struct get_capabilities_buf {
+	char pad[8];
+	struct atapi_capabilities_page cap;	/* this is 4 bytes short of ATAPI standard */
+	char extra_cap[4];			/* Acer 50X needs the regulation size buffer */
+};
+
+static
+int ide_cdrom_get_capabilities (struct cdrom_device_info *cdi, struct get_capabilities_buf *buf)
+{
+	int stat, attempts = 3, buflen = sizeof(*buf);
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_generic_command cgc;
+
+	/*
+	 * Most drives don't care about the buffer size;
+	 * they return as much info as there's room for.
+	 * But some older drives (?) had trouble with the
+	 * standard size, preferring 4 bytes less.
+	 * And the modern Acer 50X rejects anything smaller
+	 * than the standard size.
+	 */
+	if (!(drive->id && !strcmp(drive->id->model,"ATAPI CD ROM DRIVE 50X MAX")))
+		buflen -= sizeof(buf->extra_cap);	/* for all drives except Acer 50X */
+
+	do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+		if (stat == 0) {
+			/*
+			 * The ACER/AOpen 24X cdrom has the speed
+			 * fields byte-swapped from the standard.
+			 */
+			if (!(drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4))) {
+				buf->cap.curspeed = ntohs(buf->cap.curspeed);
+				buf->cap.maxspeed = ntohs(buf->cap.maxspeed);
+			}
+			CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf->cap.curspeed) + (176/2)) / 176;
+			CDROM_CONFIG_FLAGS(drive)->max_speed     = (((unsigned int)buf->cap.maxspeed) + (176/2)) / 176;
+			return 0;
+		}
+	} while (--attempts);
+	return stat;
+}
+#endif /* __ACER50__ */
+
+static
+int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
+{
+#ifndef __ACER50__
+        int stat, attempts = 3;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_generic_command cgc;
+	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
+#else
+	int stat;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_generic_command cgc;
+	struct get_capabilities_buf buf;
+#endif /* __ACER50__ */
+
+	if ((stat = cdrom_select_speed (drive, speed)) < 0)
+		return stat;
+
+	init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
+
+#ifndef __ACER50__
+	/* Now with that done, update the speed fields */
+        do {    /* we seem to get stat=0x01,err=0x00 the first time (??) */
+                if (attempts-- <= 0)
+                        return 0;
+                stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+        } while (stat);
+
+        /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+        if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
+                CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+                CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+        } else {
+                CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
+                CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+        }
+#else
+	if (ide_cdrom_get_capabilities(cdi,&buf))
+		return 0;
+#endif /* __ACER50__ */
+
+        cdi->speed = CDROM_STATE_FLAGS (drive)->current_speed;
+        return 0;
+}
+
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_info *info = drive->driver_data;
+
+	if (slot_nr == CDSL_CURRENT) {
+		struct request_sense *sense = &info->sense_data;
+		int stat = cdrom_check_status(drive);
+		if (stat == 0 || sense->sense_key == UNIT_ATTENTION)
+			return CDS_DISC_OK;
+
+		if (sense->sense_key == NOT_READY && sense->asc == 0x04 &&
+		    sense->ascq == 0x04)
+			return CDS_DISC_OK;
+
+		if (sense->sense_key == NOT_READY) {
+			/* ATAPI doesn't have anything that can help
+			   us decide whether the drive is really
+			   emtpy or the tray is just open. irk. */
+			return CDS_TRAY_OPEN;
+		}
+
+		return CDS_DRIVE_NOT_READY;
+	} else {
+		return -EINVAL;
+	}
+}
+
+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;
+
+	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))))
+		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;
+	
+	if (slot_nr == CDSL_CURRENT) {
+		(void) cdrom_check_status(drive);
+		CDROM_STATE_FLAGS (drive)->media_changed = 0;
+		return CDROM_STATE_FLAGS (drive)->media_changed;
+	} 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)
+{
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+
+static
+struct cdrom_device_ops ide_cdrom_dops = {
+	ide_cdrom_open_real,    /* open */
+	ide_cdrom_release_real, /* release */
+	ide_cdrom_drive_status, /* drive_status */
+	ide_cdrom_check_media_change_real, /* media_changed */
+	ide_cdrom_tray_move,    /* tray_move */
+	ide_cdrom_lock_door,    /* lock_door */
+	ide_cdrom_select_speed, /* select_speed */
+	NULL,			/* select_disc */
+	ide_cdrom_get_last_session, /* get_last_session */
+	ide_cdrom_get_mcn, /* get_mcn */
+	ide_cdrom_reset, /* reset */
+	ide_cdrom_audio_ioctl, /* audio_ioctl */
+	ide_cdrom_dev_ioctl,   /* dev_ioctl */
+	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_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, /* capability */
+	0, /* n_minors */
+	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;
+	int minor = (drive->select.b.unit)<<PARTN_BITS;
+
+	devinfo->dev = MKDEV (HWIF(drive)->major, minor | CD_PART_MASK);
+	devinfo->ops = &ide_cdrom_dops;
+	devinfo->mask = 0;
+	*(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
+	*(int *)&devinfo->capacity = nslots;
+	devinfo->handle = (void *) drive;
+	strcpy(devinfo->name, drive->name);
+	
+	/* set capability mask to match the probe. */
+	if (!CDROM_CONFIG_FLAGS (drive)->cd_r)
+		devinfo->mask |= CDC_CD_R;
+	if (!CDROM_CONFIG_FLAGS (drive)->cd_rw)
+		devinfo->mask |= CDC_CD_RW;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd)
+		devinfo->mask |= CDC_DVD;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd_r)
+		devinfo->mask |= CDC_DVD_R;
+	if (!CDROM_CONFIG_FLAGS (drive)->dvd_ram)
+		devinfo->mask |= CDC_DVD_RAM;
+	if (!CDROM_CONFIG_FLAGS (drive)->is_changer)
+		devinfo->mask |= CDC_SELECT_DISC;
+	if (!CDROM_CONFIG_FLAGS (drive)->audio_play)
+		devinfo->mask |= CDC_PLAY_AUDIO;
+	if (!CDROM_CONFIG_FLAGS (drive)->close_tray)
+		devinfo->mask |= CDC_CLOSE_TRAY;
+
+	devinfo->de = devfs_register (drive->de, "cd", 2, DEVFS_FL_DEFAULT,
+				      HWIF(drive)->major, minor,
+				      S_IFBLK | S_IRUGO | S_IWUGO, 0, 0,
+				      ide_fops, NULL);
+
+	return register_cdrom (devinfo);
+}
+
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+#ifndef __ACER50__
+	int stat, nslots = 1, attempts = 3;
+	struct cdrom_generic_command cgc;
+	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
+#else
+	int nslots = 1;
+	struct cdrom_generic_command cgc;
+	struct get_capabilities_buf buf;
+#endif /* __ACER50__ */
+
+	if (CDROM_CONFIG_FLAGS (drive)->nec260) {
+		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;                       
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;       
+		return nslots;
+	}
+
+	init_cdrom_command(&cgc, &buf, sizeof(buf), CGC_DATA_UNKNOWN);
+	/* 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;
+#ifndef __ACER50__
+	/* we seem to get stat=0x01,err=0x00 the first time (??) */
+	do {
+		if (attempts-- <= 0)
+			return 0;
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+	} while (stat);
+#else
+	if (ide_cdrom_get_capabilities(cdi,&buf))
+		return 0;
+#endif /* __ACER50__ */
+
+	if (buf.cap.lock == 0)
+		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+	if (buf.cap.eject)
+		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
+	if (buf.cap.cd_r_write)
+		CDROM_CONFIG_FLAGS (drive)->cd_r = 1;
+	if (buf.cap.cd_rw_write)
+		CDROM_CONFIG_FLAGS (drive)->cd_rw = 1;
+	if (buf.cap.test_write)
+		CDROM_CONFIG_FLAGS (drive)->test_write = 1;
+	if (buf.cap.dvd_ram_read || buf.cap.dvd_r_read || buf.cap.dvd_rom)
+		CDROM_CONFIG_FLAGS (drive)->dvd = 1;
+	if (buf.cap.dvd_ram_write)
+		CDROM_CONFIG_FLAGS (drive)->dvd_r = 1;
+	if (buf.cap.dvd_r_write)
+		CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1;
+	if (buf.cap.audio_play)
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
+	if (buf.cap.mechtype == 0)
+		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
+
+#if ! STANDARD_ATAPI
+	if (cdi->sanyo_slot > 0) {
+		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+		nslots = 3;
+	}
+
+	else
+#endif /* not STANDARD_ATAPI */
+	if (buf.cap.mechtype == mechtype_individual_changer ||
+	    buf.cap.mechtype == mechtype_cartridge_changer) {
+		if ((nslots = cdrom_number_of_slots(cdi)) > 1) {
+			CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+			CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1;
+		}
+	}
+
+#ifndef __ACER50__
+	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+	if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
+		CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+	} else {
+		CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+	}
+#endif /* __ACER50__ */
+
+	/* don't print speed if the drive reported 0.
+	 */
+	printk("%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)? "AM" : "");
+
+        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(buf.cap.buffer_size));
+
+	if (drive->using_dma) {
+		if ((drive->id->field_valid & 4) &&
+		    (drive->id->hw_config & 0x2000) &&
+		    (HWIF(drive)->udma_four) &&
+		    (drive->id->dma_ultra & (drive->id->dma_ultra >> 11) & 3)) {
+			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
+		} else if ((drive->id->field_valid & 4) &&
+			   (drive->id->dma_ultra & (drive->id->dma_ultra >> 8) & 7)) {
+			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
+		} else if (drive->id->field_valid & 4) {
+			printk(", (U)DMA");	/* Can be BIOS-enabled! */
+		} else {
+			printk(", DMA");
+		}
+	}
+	printk("\n");
+
+	return nslots;
+}
+
+static void ide_cdrom_add_settings(ide_drive_t *drive)
+{
+	int major = HWIF(drive)->major;
+	int minor = drive->select.b.unit << PARTN_BITS;
+
+	ide_add_setting(drive,	"breada_readahead",	SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+	ide_add_setting(drive,	"file_readahead",	SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor],	NULL);
+	ide_add_setting(drive,	"max_kb_per_request",	SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
+}
+
+static
+int ide_cdrom_setup (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	int minor = drive->select.b.unit << PARTN_BITS;
+	int nslots;
+
+	set_device_ro(MKDEV(HWIF(drive)->major, minor), 1);
+	set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE);
+
+	drive->special.all	= 0;
+	drive->ready_stat	= 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
+
+	if (drive->id != NULL)
+		CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
+			((drive->id->config & 0x0060) == 0x20);
+	else
+		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
+
+	CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
+	CDROM_CONFIG_FLAGS (drive)->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;
+	if (drive->id != NULL) {
+		/* 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 (drive->id != NULL) {
+		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);
+
+	if (ide_cdrom_register (drive, nslots)) {
+		printk ("%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;
+}
+
+/* Forwarding functions to generic routines. */
+static
+int ide_cdrom_ioctl (ide_drive_t *drive,
+		     struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	return cdrom_fops.ioctl (inode, file, cmd, arg);
+}
+
+static
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	int rc;
+
+	MOD_INC_USE_COUNT;
+	if (info->buffer == NULL)
+		info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL);
+	rc = cdrom_fops.open (ip, fp);
+	if (rc) {
+		drive->usage--;
+		MOD_DEC_USE_COUNT;
+	}
+	return rc;
+}
+
+static
+void ide_cdrom_release (struct inode *inode, struct file *file,
+			ide_drive_t *drive)
+{
+	cdrom_fops.release (inode, file);
+	MOD_DEC_USE_COUNT;
+}
+
+static
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+	return cdrom_fops.check_media_change
+		(MKDEV (HWIF (drive)->major,
+			(drive->select.b.unit)<<PARTN_BITS));
+}
+
+static
+int ide_cdrom_cleanup(ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *devinfo = &info->devinfo;
+
+	if (ide_unregister_subdriver (drive))
+		return 1;
+	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 ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
+	kfree(info);
+	drive->driver_data = NULL;
+	return 0;
+}
+
+static ide_driver_t ide_cdrom_driver = {
+	"ide-cdrom",			/* name */
+	IDECD_VERSION,			/* version */
+	ide_cdrom,			/* media */
+	0,				/* busy */
+	1,				/* supports_dma */
+	1,				/* supports_dsc_overlap */
+	ide_cdrom_cleanup,		/* cleanup */
+	ide_do_rw_cdrom,		/* do_request */
+	NULL,				/* ??? or perhaps cdrom_end_request? */
+	ide_cdrom_ioctl,		/* ioctl */
+	ide_cdrom_open,			/* open */
+	ide_cdrom_release,		/* release */
+	ide_cdrom_check_media_change,	/* media_change */
+	NULL,				/* pre_reset */
+	NULL,				/* capacity */
+	NULL,				/* special */
+	NULL				/* proc */
+};
+
+int ide_cdrom_init (void);
+static ide_module_t ide_cdrom_module = {
+	IDE_DRIVER_MODULE,
+	ide_cdrom_init,
+	&ide_cdrom_driver,
+	NULL
+};
+
+/* options */
+char *ignore = NULL;
+
+#ifdef MODULE
+MODULE_PARM(ignore, "s");
+MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
+
+void __exit ide_cdrom_exit(void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+
+	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL)
+		if (ide_cdrom_cleanup (drive)) {
+			printk ("%s: cleanup_module() called while still busy\n", drive->name);
+			failed++;
+		}
+	ide_unregister_module (&ide_cdrom_module);
+}
+#endif /* MODULE */
+ 
+int ide_cdrom_init (void)
+{
+	ide_drive_t *drive;
+	struct cdrom_info *info;
+	int failed = 0;
+
+	MOD_INC_USE_COUNT;
+	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
+		/* skip drives that we were told to ignore */
+		if (ignore != NULL) {
+			if (strstr(ignore, drive->name)) {
+				printk("ide-cd: ignoring drive %s\n", drive->name);
+				continue;
+			}
+		}
+		if (drive->scsi) {
+			printk("ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name);
+			continue;
+		}
+		info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
+		if (info == NULL) {
+			printk ("%s: Can't allocate a cdrom structure\n", drive->name);
+			continue;
+		}
+		if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) {
+			printk ("%s: Failed to register the driver with ide.c\n", drive->name);
+			kfree (info);
+			continue;
+		}
+		memset (info, 0, sizeof (struct cdrom_info));
+		drive->driver_data = info;
+		DRIVER(drive)->busy++;
+		if (ide_cdrom_setup (drive)) {
+			DRIVER(drive)->busy--;
+			if (ide_cdrom_cleanup (drive))
+				printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name);
+			continue;
+		}
+		DRIVER(drive)->busy--;
+		failed--;
+	}
+	ide_register_module(&ide_cdrom_module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+module_init(ide_cdrom_init);
+module_exit(ide_cdrom_exit);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)