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

Next file: linux/drivers/block/ide-disk.c
Previous file: linux/drivers/block/genhd.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.7/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
@@ -108,6 +108,7 @@
  * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
  * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
  *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
+ * 3.17a Oct 31, 1996 -- Added module and DMA support.
  *
  * NOTE: Direct audio reads will only work on some types of drive.
  * So far, i've received reports of success for Sony and Toshiba drives.
@@ -123,6 +124,7 @@
 
 /***************************************************************************/
 
+#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
@@ -255,41 +257,127 @@
 #define CDROM_STATE_FLAGS(drive)  ((struct ide_cd_state_flags *)&((drive)->bios_head))
 
 
-#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
+struct atapi_request_sense {
+	unsigned char error_code : 7;
+	unsigned char valid      : 1;
+	byte reserved1;
+	unsigned char sense_key  : 4;
+	unsigned char reserved2  : 1;
+	unsigned char ili        : 1;
+	unsigned char reserved3  : 2;
+	byte info[4];
+	byte sense_len;
+	byte command_info[4];
+	byte asc;
+	byte ascq;
+	byte fru;
+	byte sense_key_specific[3];
+};
 
+struct packet_command {
+	char *buffer;
+	int buflen;
+	int stat;
+	struct atapi_request_sense *sense_data;
+	unsigned char c[12];
+};
 
-
-/****************************************************************************
- * Routines to read and write data from/to the drive, using
- * the routines input_ide_data() and output_ide_data() from ide.c.
- *
- * These routines will round up any request for an odd number of bytes,
- * so if an odd bytecount is specified, be sure that there's at least one
- * extra byte allocated for the buffer.
- */
 
+/* Structure of a MSF cdrom address. */
+struct atapi_msf {
+	byte reserved;
+	byte minute;
+	byte second;
+	byte frame;
+};
 
-static inline
-void cdrom_in_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
-{
-	++bytecount;
-	ide_input_data (drive, buffer, bytecount / 4);
-	if ((bytecount & 0x03) >= 2) {
-		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
-	}
-}
 
+/* Space to hold the disk TOC. */
 
-static inline
-void cdrom_out_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
-{
-	++bytecount;
-	ide_output_data (drive, buffer, bytecount / 4);
-	if ((bytecount & 0x03) >= 2) {
-		outsw (IDE_DATA_REG,
-		       ((byte *)buffer) + (bytecount & ~0x03), 1);
-	}
-}
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+	unsigned short toc_length;
+	byte first_track;
+	byte last_track;
+};
+
+struct atapi_toc_entry {
+	byte reserved1;
+	unsigned control : 4;
+	unsigned adr     : 4;
+	byte track;
+	byte reserved2;
+	union {
+		unsigned lba;
+		struct atapi_msf msf;
+	} addr;
+};
+
+struct atapi_toc {
+	int    last_session_lba;
+	int    xa_flag;
+	unsigned capacity;
+	struct atapi_toc_header hdr;
+	struct atapi_toc_entry  ent[MAX_TRACKS+1];
+	  /* One extra for the leadout. */
+};
+
+
+/* This structure is annoyingly close to, but not identical with,
+   the cdrom_subchnl structure from cdrom.h. */
+struct atapi_cdrom_subchnl 
+{
+	u_char  acdsc_reserved;
+	u_char  acdsc_audiostatus;
+	u_short acdsc_length;
+	u_char  acdsc_format;
+
+	u_char  acdsc_adr:	4;
+	u_char  acdsc_ctrl:	4;
+	u_char  acdsc_trk;
+	u_char  acdsc_ind;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_absaddr;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_reladdr;
+};
+
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+	/* Buffer for table of contents.  NULL if we haven't allocated
+	   a TOC buffer for this device yet. */
+
+	struct atapi_toc *toc;
+
+	/* Sector buffer.  If a read request wants only the first part
+	   of a cdrom block, we cache the rest of the block here,
+	   in the expectation that that data is going to be wanted soon.
+	   SECTOR_BUFFERED is the number of the first buffered sector,
+	   and NSECTORS_BUFFERED is the number of sectors in the buffer.
+	   Before the buffer is allocated, we should have
+	   SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
+
+	unsigned long sector_buffered;
+	unsigned long nsectors_buffered;
+	char *sector_buffer;
+
+	/* The result of the last successful request sense command
+	   on this device. */
+	struct atapi_request_sense sense_data;
+
+	struct request request_sense_request;
+	struct packet_command request_sense_pc;
+	int dma;
+};
+
+
+#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
 
 
 
@@ -573,6 +661,7 @@
 				       struct atapi_request_sense *reqbuf,
 				       struct packet_command *failed_command)
 {
+	struct cdrom_info *info = drive->driver_data;
 	struct request *rq;
 	struct packet_command *pc;
 	int len;
@@ -580,11 +669,11 @@
 	/* If the request didn't explicitly specify where
 	   to put the sense data, use the statically allocated structure. */
 	if (reqbuf == NULL)
-		reqbuf = &drive->cdrom_info.sense_data;
+		reqbuf = &info->sense_data;
 
 	/* Make up a new request to retrieve sense information. */
 
-	pc = &HWIF(drive)->request_sense_pc;
+	pc = &info->request_sense_pc;
 	memset (pc, 0, sizeof (*pc));
 
 	/* The request_sense structure has an odd number of (16-bit) words,
@@ -602,7 +691,7 @@
 
 	/* stuff the sense request in front of our current request */
 
-	rq = &HWIF(drive)->request_sense_request;
+	rq = &info->request_sense_request;
 	ide_init_drive_cmd (rq);
 	rq->cmd = REQUEST_SENSE_COMMAND;
 	rq->buffer = (char *)pc;
@@ -641,9 +730,11 @@
    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;
-	drive->cdrom_info.nsectors_buffered = 0;
+	info->nsectors_buffered = 0;
 }
 
 
@@ -792,11 +883,16 @@
 static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
 				       ide_handler_t *handler)
 {
+	struct cdrom_info *info = drive->driver_data;
+
 	/* Wait for the controller to be idle. */
 	if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1;
 
+	if (info->dma)
+		info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive);
+
 	/* Set up the controller registers. */
-	OUT_BYTE (0, IDE_FEATURE_REG);
+	OUT_BYTE (info->dma, IDE_FEATURE_REG);
 	OUT_BYTE (0, IDE_NSECTOR_REG);
 	OUT_BYTE (0, IDE_SECTOR_REG);
 
@@ -804,6 +900,9 @@
 	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
 	OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
 
+	if (info->dma)
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
 		ide_set_handler (drive, handler, WAIT_CMD);
 		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
@@ -842,7 +941,7 @@
 	ide_set_handler (drive, handler, WAIT_CMD);
 
 	/* Send the command to the device. */
-	cdrom_out_bytes (drive, cmd_buf, cmd_len);
+	atapi_output_bytes (drive, cmd_buf, cmd_len);
 
 	return 0;
 }
@@ -863,7 +962,7 @@
 static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
                                   int sectors_to_transfer)
 {
-	struct cdrom_info *info = &drive->cdrom_info;
+	struct cdrom_info *info = drive->driver_data;
 
 	/* Number of sectors to read into the buffer. */
 	int sectors_to_buffer = MIN (sectors_to_transfer,
@@ -892,7 +991,7 @@
 	/* Read the data into the buffer. */
 	dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
 	while (sectors_to_buffer > 0) {
-		cdrom_in_bytes (drive, dest, SECTOR_SIZE);
+		atapi_input_bytes (drive, dest, SECTOR_SIZE);
 		--sectors_to_buffer;
 		--sectors_to_transfer;
 		++info->nsectors_buffered;
@@ -902,7 +1001,7 @@
 	/* Throw away any remaining data. */
 	while (sectors_to_transfer > 0) {
 		char dum[SECTOR_SIZE];
-		cdrom_in_bytes (drive, dum, sizeof (dum));
+		atapi_input_bytes (drive, dum, sizeof (dum));
 		--sectors_to_transfer;
 	}
 }
@@ -929,7 +1028,7 @@
 		   and quit this request. */
 		while (len > 0) {
 			int dum = 0;
-			cdrom_out_bytes (drive, &dum, sizeof (dum));
+			atapi_output_bytes (drive, &dum, sizeof (dum));
 			len -= sizeof (dum);
 		}
 	} else {
@@ -950,12 +1049,34 @@
 {
 	int stat;
 	int ireason, len, sectors_to_transfer, nskip;
+	struct cdrom_info *info = drive->driver_data;
+	int i, dma = info->dma, dma_error = 0;
 
 	struct request *rq = HWGROUP(drive)->rq;
 
 	/* Check for errors. */
+	if (dma) {
+		info->dma = 0;
+		if ((dma_error = HWIF(drive)->dmaproc(ide_dma_status_bad, drive))) {
+			printk ("%s: disabled DMA\n", drive->name);
+			drive->using_dma = 0;
+		}
+		(void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
+	}
+
 	if (cdrom_decode_status (drive, 0, &stat)) return;
 
+	if (dma) {
+		if (!dma_error) {
+			for (i = rq->nr_sectors; i > 0;) {
+				i -= rq->current_nr_sectors;
+				ide_end_request(1, HWGROUP(drive));
+			}
+		} else
+			ide_error (drive, "dma error", stat);
+		return;
+	}
+
 	/* Read the interrupt reason and the transfer length. */
 	ireason = IN_BYTE (IDE_NSECTOR_REG);
 	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
@@ -1000,7 +1121,7 @@
 	while (nskip > 0) {
 		/* We need to throw away a sector. */
 		char dum[SECTOR_SIZE];
-		cdrom_in_bytes (drive, dum, sizeof (dum));
+		atapi_input_bytes (drive, dum, sizeof (dum));
 
 		--rq->current_nr_sectors;
 		--nskip;
@@ -1033,8 +1154,8 @@
 			/* Read this_transfer sectors
 			   into the current buffer. */
 			while (this_transfer > 0) {
-				cdrom_in_bytes (drive
-						, rq->buffer, SECTOR_SIZE);
+				atapi_input_bytes (drive,
+						   rq->buffer, SECTOR_SIZE);
 				rq->buffer += SECTOR_SIZE;
 				--rq->nr_sectors;
 				--rq->current_nr_sectors;
@@ -1057,7 +1178,7 @@
  */
 static int cdrom_read_from_buffer (ide_drive_t *drive)
 {
-	struct cdrom_info *info = &drive->cdrom_info;
+	struct cdrom_info *info = drive->driver_data;
 	struct request *rq = HWGROUP(drive)->rq;
 
 	/* Can't do anything if there's no buffer. */
@@ -1177,6 +1298,7 @@
  */
 static void 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);
 
@@ -1197,7 +1319,12 @@
 		return;
 
 	/* Clear the local sector buffer. */
-	drive->cdrom_info.nsectors_buffered = 0;
+	info->nsectors_buffered = 0;
+
+	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. */
 	cdrom_start_packet_command (drive, 32768,
@@ -1274,13 +1401,13 @@
 		}
 
 		/* Transfer the data. */
-		cdrom_out_bytes (drive, pc->buffer, thislen);
+		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;
-			cdrom_out_bytes (drive, &dum, sizeof (dum));
+			atapi_output_bytes (drive, &dum, sizeof (dum));
 			len -= sizeof (dum);
 		}
 
@@ -1301,13 +1428,13 @@
 		}
 
 		/* Transfer the data. */
-		cdrom_in_bytes (drive, pc->buffer, thislen);
+		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;
-			cdrom_in_bytes (drive, &dum, sizeof (dum));
+			atapi_input_bytes (drive, &dum, sizeof (dum));
 			len -= sizeof (dum);
 		}
 
@@ -1342,6 +1469,9 @@
 	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;
 
 	len = pc->buflen;
 	if (len < 0) len = -len;
@@ -1432,10 +1562,8 @@
  * cdrom driver request routine.
  */
 
-void ide_do_rw_cdrom (ide_drive_t *drive, unsigned long block)
+void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
-	struct request *rq = HWGROUP(drive)->rq;
-
 	if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
 		cdrom_do_packet_command (drive);
 	else if (rq -> cmd == RESET_DRIVE_COMMAND) {
@@ -1670,7 +1798,8 @@
 		struct atapi_request_sense *reqbuf)
 {
 	int stat, ntracks, i;
-	struct atapi_toc *toc = drive->cdrom_info.toc;
+	struct cdrom_info *info = drive->driver_data;
+	struct atapi_toc *toc = info->toc;
 	struct {
 		struct atapi_toc_header hdr;
 		struct atapi_toc_entry  ent;
@@ -1680,7 +1809,7 @@
 		/* Try to allocate space. */
 		toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
 						    GFP_KERNEL);
-		drive->cdrom_info.toc = toc;
+		info->toc = toc;
 	}
 
 	if (toc == NULL) {
@@ -1904,6 +2033,7 @@
                          struct atapi_toc_entry **ent,
 			 struct atapi_request_sense *reqbuf)
 {
+	struct cdrom_info *info = drive->driver_data;
 	int stat, ntracks;
 	struct atapi_toc *toc;
 
@@ -1911,7 +2041,7 @@
 	stat = cdrom_read_toc (drive, reqbuf);
 	if (stat) return stat;
 
-	toc = drive->cdrom_info.toc;
+	toc = info->toc;
 
 	/* Check validity of requested track number. */
 	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
@@ -2026,6 +2156,8 @@
 int ide_cdrom_ioctl (ide_drive_t *drive, struct inode *inode,
 		     struct file *file, unsigned int cmd, unsigned long arg)
 {
+	struct cdrom_info *info = drive->driver_data;
+	
 	switch (cmd) {
 	case CDROMEJECT: {
 		int stat;
@@ -2133,7 +2265,7 @@
 		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
 
-		toc = drive->cdrom_info.toc;
+		toc = info->toc;
 		tochdr.cdth_trk0 = toc->hdr.first_track;
 		tochdr.cdth_trk1 = toc->hdr.last_track;
 
@@ -2300,7 +2432,7 @@
 		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
 
-		toc = drive->cdrom_info.toc;
+		toc = info->toc;
 
 		if (ms_info.addr_format == CDROM_MSF)
 			lba_to_msf (toc->last_session_lba,
@@ -2330,7 +2462,7 @@
 		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
 
-		toc = drive->cdrom_info.toc;
+		toc = info->toc;
 
 		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
 		if (stat) return stat;
@@ -2411,7 +2543,7 @@
 		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
 
-		toc = drive->cdrom_info.toc;
+		toc = info->toc;
 
 		if (lba < 0 || lba >= toc->capacity)
 			return -EINVAL;
@@ -2569,6 +2701,8 @@
 		return -EROFS;
 	}
 
+	MOD_INC_USE_COUNT;
+
 	/* If this is the first open, check the drive status. */
 	if (drive->usage == 1) {
 		int stat;
@@ -2613,6 +2747,7 @@
 		if (CDROM_STATE_FLAGS (drive)->eject_on_close)
 			(void) cdrom_eject (drive, 0, NULL);
 	}
+	MOD_DEC_USE_COUNT;
 }
 
 
@@ -2623,6 +2758,8 @@
 
 void ide_cdrom_setup (ide_drive_t *drive)
 {
+	struct cdrom_info *info = drive->driver_data;
+		
 	blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] =
 		CD_FRAMESIZE;
 
@@ -2705,12 +2842,97 @@
 	}
 #endif /* not STANDARD_ATAPI */
 
-	drive->cdrom_info.toc               = NULL;
-	drive->cdrom_info.sector_buffer     = NULL;
-	drive->cdrom_info.sector_buffered   = 0;
-	drive->cdrom_info.nsectors_buffered = 0;
+	info->toc               = NULL;
+	info->sector_buffer     = NULL;
+	info->sector_buffered   = 0;
+	info->nsectors_buffered = 0;
+}
+
+int ide_cdrom_cleanup(ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	if (ide_unregister_subdriver (drive))
+		return 1;
+	if (info->sector_buffer != NULL)
+		kfree (info->sector_buffer);
+	if (info->toc != NULL)
+		kfree (info->toc);
+	kfree (info);
+	drive->driver_data = NULL;
+	return 0;
+}
+
+int ide_cdrom_init (void);
+static ide_module_t ide_cdrom_module = {
+	IDE_DRIVER_MODULE,
+	ide_cdrom_init,
+	NULL
+};
+
+static ide_driver_t ide_cdrom_driver = {
+	ide_cdrom,			/* media */
+	0,				/* busy */
+	1,				/* supports_dma */
+	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 */
+};
+
+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, NULL, failed++)) != NULL) {
+		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;
+		}
+		failed--;
+		memset (info, 0, sizeof (struct cdrom_info));
+		drive->driver_data = info;
+		ide_cdrom_setup (drive);
+	}
+	ide_register_module(&ide_cdrom_module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return ide_cdrom_init();
 }
 
+void cleanup_module(void)
+{
+	ide_drive_t *drive;
+	int failed = 0;
+
+	while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
+		if (ide_cdrom_cleanup (drive)) {
+			printk ("%s: cleanup_module() called while still busy\n", drive->name);
+			failed++;
+		}
+	ide_unregister_module (&ide_cdrom_module);
+}
+#endif /* MODULE */
 
 
 /*
@@ -2720,7 +2942,6 @@
  *  Query the drive to find what features are available
  *   before trying to use them.
  *  Integrate spindown time adjustment patch.
- *  Modularize.
  *  CDROMRESET ioctl.
  *  Better support for changers.
  */

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