patch-1.3.63 linux/drivers/block/ide-tape.c

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

diff -u --recursive --new-file v1.3.62/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-tape.c	Version 1.2 - ALPHA	Jan   1, 1996
+ * linux/drivers/block/ide-tape.c	Version 1.3 - ALPHA	Feb   9, 1996
  *
  * Copyright (C) 1995, 1996 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
  *
@@ -53,6 +53,8 @@
  *		integral number of the tape's recommended data transfer unit
  *		(which is shown on initialization and can be received with
  *		 an ioctl).
+ *		As of version 1.3 of the driver, this is no longer as critical
+ *		as it used to be.
  *	3.	No buffering is performed by the user backup program.
  *
  * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
@@ -161,6 +163,23 @@
  *                       The recommended user block size is returned by
  *                        the MTIOCGET ioctl.
  *                       Additional minor changes.
+ * Ver 1.3   Feb  9  96  Fixed pipelined read mode bug which prevented the
+ *                        use of some block sizes during a restore procedure.
+ *                       The character device interface will now present a
+ *                        continuous view of the media - any mix of block sizes
+ *                        during a backup/restore procedure is supported. The
+ *                        driver will buffer the requests internally and
+ *                        convert them to the tape's recommended transfer
+ *                        unit, making performance almost independent of the
+ *                        chosen user block size.
+ *                       Some improvements in error recovery.
+ *                       By cooperating with triton.c, bus mastering DMA can
+ *                        now sometimes be used with IDE tape drives as well.
+ *                        Bus mastering DMA has the potential to dramatically
+ *                        reduce the CPU's overhead when accessing the device,
+ *                        and can be enabled by using hdparm -d1 on the tape's
+ *                        block device interface. For more info, read the
+ *                        comments in triton.c.
  *
  * We are currently in an *alpha* stage. The driver is not complete and not
  * much tested. I would strongly suggest to:
@@ -279,6 +298,7 @@
  *		sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
  */
 
+#include <linux/config.h>
 #include <linux/hdreg.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -351,11 +371,6 @@
  *	device interface.
  */
 
-#define IDETAPE_INQUIRY_IOCTL		0x0341
-#define	IDETAPE_LOCATE_IOCTL		0x0342
-
-#define IDETAPE_RESET_IOCTL		0x0350
-
 /*
  *	Special requests for our block device strategy routine.
  *
@@ -663,13 +678,22 @@
 typedef struct {
 	unsigned error_code	:7;	/* Current of deferred errors */
 	unsigned valid		:1;	/* The information field conforms to QIC-157C */
-	byte reserved_1;		/* Segment Number - Reserved */
+	unsigned reserved_1	:8;	/* Segment Number - Reserved */
 	unsigned sense_key	:4;	/* Sense Key */
 	unsigned reserved2_4	:1;	/* Reserved */
 	unsigned ili		:1;	/* Incorrect Length Indicator */
 	unsigned eom		:1;	/* End Of Medium */
-	unsigned filemark	:1;	/* Filemark */
-	unsigned long information;	/* Information - Command specific */
+	unsigned filemark 	:1;	/* Filemark */
+
+	/*
+	 *	We can't use a 32 bit variable, since it will be re-aligned
+	 *	by GCC, as we are not on a 32 bit boundary.
+	 */
+
+	byte information1;		/* MSB - Information - Command specific */
+	byte information2;
+	byte information3;
+	byte information4;		/* LSB */
 	byte asl;			/* Additional sense length (n-7) */
 	unsigned long command_specific; /* Additional command specific information */
 	byte asc;			/* Additional Sense Code */
@@ -679,6 +703,7 @@
 	unsigned sksv		:1;	/* Sense Key Specific informatio is valid */
 	byte sk_specific2;		/* Sense Key Specific */
 	byte sk_specific3;		/* Sense Key Specific */
+	byte pad [2];			/* Padding to 20 bytes */
 } idetape_request_sense_result_t;
 
 /*
@@ -817,13 +842,6 @@
 struct request *idetape_next_rq_storage (ide_drive_t *drive);
 
 /*
- *	idetape_end_request is used to finish servicing a request, and to
- *	insert a pending pipeline request into the main device queue.
- */
- 
-void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
-
-/*
  *	Various packet commands
  */
  
@@ -857,9 +875,7 @@
  */
 
 int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count);
-int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count);
 int idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, int count);
-int idetape_chrdev_write_remainder (struct inode *inode, struct file *file, const char *buf, int count);
 int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
 int idetape_chrdev_open (struct inode *inode, struct file *file);
 void idetape_chrdev_release (struct inode *inode,struct file *file);
@@ -927,6 +943,8 @@
 unsigned long idetape_swap_long (unsigned long temp);
 unsigned short idetape_swap_short (unsigned short temp);
 
+#define IDETAPE_MIN(a,b)	((a)<(b) ? (a):(b))
+
 /*
  *	Pipeline related functions
  */
@@ -939,7 +957,8 @@
 void idetape_add_stage_tail (ide_drive_t *drive,idetape_pipeline_stage_t *stage);
 void idetape_remove_stage_head (ide_drive_t *drive);
 void idetape_active_next_stage (ide_drive_t *drive);
-void idetape_empty_read_pipeline (ide_drive_t *drive);
+void idetape_wait_for_pipeline (ide_drive_t *drive);
+void idetape_discard_read_pipeline (ide_drive_t *drive);
 void idetape_empty_write_pipeline (ide_drive_t *drive);
 void idetape_insert_pipeline_into_queue (ide_drive_t *drive);
 
@@ -1223,6 +1242,7 @@
 	tape->error_in_pipeline_stage=0;
 	tape->request_status=0;
 	tape->chrdev_direction=idetape_direction_none;
+	tape->reset_issued=0;
 
 #if IDETAPE_PIPELINE
 	tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
@@ -1240,16 +1260,22 @@
 	if (tape->data_buffer_size % IDETAPE_ALLOCATION_BLOCK)
 		allocation_length+=IDETAPE_ALLOCATION_BLOCK;
 	
+#if IDETAPE_MINIMIZE_IDLE_MEMORY_USAGE
+	tape->data_buffer=tape->merge_buffer=NULL;
+#else
 	tape->data_buffer=kmalloc (allocation_length,GFP_KERNEL);
-	tape->temp_data_buffer=kmalloc (allocation_length,GFP_KERNEL);
-	if (tape->data_buffer == NULL || tape->temp_data_buffer == NULL) {
+	tape->merge_buffer=kmalloc (allocation_length,GFP_KERNEL);
+	if (tape->data_buffer == NULL || tape->merge_buffer == NULL) {
 		printk ("ide-tape: FATAL - Can not allocate 2 buffers of %d bytes each\n",allocation_length);
 		printk ("ide-tape: Aborting character device installation\n");
 		idetape_drive_already_found=0;
 		unregister_chrdev (idetape_chrdev.major,idetape_chrdev.name);
 		return;
 	}
+#endif /* IDETAPE_MINIMIZE_IDLE_MEMORY_USAGE */
 
+	tape->merge_buffer_size=tape->merge_buffer_offset=0;
+	
 #if IDETAPE_ANTICIPATE_READ_WRITE_DSC
 
 	/*
@@ -1428,13 +1454,14 @@
 {
 	idetape_tape_t *tape;
 	idetape_bcount_reg_t bcount;
-        idetape_ireason_reg_t ireason;
+	idetape_ireason_reg_t ireason;
+	int dma_ok=0;
 
 	tape=&(drive->tape);
 	        
 #if IDETAPE_DEBUG_BUGS
 	if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
-		printk ("ide-tape: ide-tape.c bug - Two request sense in serial were issued\n");
+		printk ("ide-tape: possible ide-tape.c bug - Two request sense in serial were issued\n");
 	}
 #endif /* IDETAPE_DEBUG_BUGS */
 
@@ -1447,7 +1474,8 @@
 		/*
 		 *	We will "abort" retrying a packet command in case
 		 *	a legitimate error code was received (crossing a
-		 *	filemark, for example). We will not log those errors.
+		 *	filemark, or DMA error in the end of media, for
+		 *	example).
 		 */
 
 		if (!pc->abort) {
@@ -1478,10 +1506,21 @@
  */
  
 	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;	
 	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
 
-									/* Initialize the task file registers */
-	OUT_BYTE (0,IDETAPE_FEATURES_REG);				/* Use PIO data transger, No DMA */
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (pc->dma_error) {
+		printk ("ide-tape: DMA disabled, reverting to PIO\n");
+		drive->using_dma=0;
+		pc->dma_error=0;
+	}
+	if (pc->request_transfer && pc->dma_recommended && drive->using_dma) {
+		dma_ok=!(HWIF(drive)->dmaproc(pc->writing ? ide_dma_write : ide_dma_read, drive));
+	}		
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+	OUT_BYTE (dma_ok ? 1:0,IDETAPE_FEATURES_REG);			/* Use PIO/DMA */
 	OUT_BYTE (bcount.b.high,IDETAPE_BCOUNTH_REG);
 	OUT_BYTE (bcount.b.low,IDETAPE_BCOUNTL_REG);
 	OUT_BYTE (drive->select.all,IDETAPE_DRIVESEL_REG);
@@ -1498,17 +1537,24 @@
 		 *	here anyway.
 		 */
 		 
-		 printk ("ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
-		 return;
+		printk ("ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return;
 	}
 	
 	ireason.all=IN_BYTE (IDETAPE_IREASON_REG);
 	if (!ireason.b.cod || ireason.b.io) {
 		printk ("ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
-		/* ??? */
+		ide_do_reset (drive);
+		return;		
 	}
 		
 	ide_output_data (drive,pc->c,12/4);			/* Send the actual packet */
+#ifdef CONFIG_BLK_DEV_TRITON
+	if ((pc->dma_in_progress=dma_ok)) {			/* Begin DMA, if necessary */
+		pc->dma_error=0;
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
 }
 
 /*
@@ -1527,10 +1573,27 @@
 	idetape_tape_t *tape=&(drive->tape);
 	idetape_status_reg_t status;
 	idetape_bcount_reg_t bcount;
-        idetape_ireason_reg_t ireason;
+	idetape_ireason_reg_t ireason;
 	idetape_packet_command_t *pc=tape->pc;
 	unsigned long temp;
 
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (pc->dma_in_progress) {
+		if ((pc->dma_error=HWIF(drive)->dmaproc(ide_dma_status_bad, drive)))
+			/*
+			 *	We will currently correct the following in
+			 *	idetape_analyze_error.
+			 */
+			pc->actually_transferred=HWIF(drive)->dmaproc(ide_dma_transferred, drive);
+		else
+			pc->actually_transferred=pc->request_transfer;
+		(void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));	/* End DMA */
+#if IDETAPE_DEBUG_LOG
+		printk ("ide-tape: DMA finished\n");
+#endif /* IDETAPE_DEBUG_LOG */
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
 	status.all=IN_BYTE (IDETAPE_STATUS_REG);		/* Clear the interrupt */
 
 #if IDETAPE_DEBUG_LOG
@@ -1542,10 +1605,11 @@
 		printk ("Packet command completed\n");
 		printk ("Total bytes transferred: %lu\n",pc->actually_transferred);
 #endif /* IDETAPE_DEBUG_LOG */
-		
+		pc->dma_in_progress=0;
+						
 		sti ();
 
-		if (status.b.check) {					/* Error detected */
+		if (status.b.check || pc->dma_error) {			/* Error detected */
 #if IDETAPE_DEBUG_LOG
 	/*
 	 *	Without debugging, we only log an error if we decided to
@@ -1553,6 +1617,12 @@
 	 */
 			printk ("ide-tape: %s: I/O error, ",drive->name);
 #endif /* IDETAPE_DEBUG_LOG */
+			if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+				printk ("ide-tape: I/O error in request sense command\n");
+				ide_do_reset (drive);
+				return;
+			}			
+						
 			idetape_retry_pc (drive);			/* Retry operation */
 			return;
 		}
@@ -1572,19 +1642,30 @@
 			(*pc->callback)(drive);			/* Command finished - Call the callback function */
 		return;
 	}
-
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (pc->dma_in_progress) {
+		pc->dma_in_progress=0;
+		printk ("ide-tape: The tape wants to issue more interrupts in DMA mode\n");
+		printk ("ide-tape: DMA disabled, reverting to PIO\n");
+		drive->using_dma=0;
+		ide_do_reset (drive);
+		return;
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
 	bcount.b.high=IN_BYTE (IDETAPE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
 	bcount.b.low=IN_BYTE (IDETAPE_BCOUNTL_REG);			/* on this interrupt */
 	ireason.all=IN_BYTE (IDETAPE_IREASON_REG);			/* Read the interrupt reason register */
 
 	if (ireason.b.cod) {
 		printk ("ide-tape: CoD != 0 in idetape_pc_intr\n");
-		/* ??? */
+		ide_do_reset (drive);
+		return;
 	}
 	if (ireason.b.io != !(pc->writing)) {			/* Hopefully, we will never get here */
 		printk ("ide-tape: We wanted to %s, ",pc->writing ? "Write":"Read");
 		printk ("but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
-		/* ??? */		
+		ide_do_reset (drive);
+		return;
 	}
 	
 	if (!pc->writing) {					/* Reading - Check that we have enough space */
@@ -2058,24 +2139,19 @@
 void idetape_read_callback (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
-	struct request *rq;
+	idetape_tape_t *tape=&(drive->tape);
+	struct request *rq=HWGROUP(drive)->rq;
+	int blocks_read=tape->pc->actually_transferred/tape->tape_block_size;
 
-	tape=&(drive->tape);	
-	rq=HWGROUP(drive)->rq;
 #if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_read_callback\n");
 #endif /* IDETAPE_DEBUG_LOG */
-	tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
-	if (!tape->pc->error) {
-#if IDETAPE_DEBUG_LOG
-		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG_LOG */
-		rq->sector+=rq->current_nr_sectors;
-		rq->nr_sectors-=rq->current_nr_sectors;
-		rq->current_nr_sectors=0;
+
+	tape->block_address+=blocks_read;
+	rq->current_nr_sectors-=blocks_read;	
+
+	if (!tape->pc->error)
 		idetape_end_request (1,HWGROUP (drive));
-	}
 	else {
 		rq->errors=tape->pc->error;
 		switch (rq->errors) {
@@ -2091,25 +2167,21 @@
 void idetape_write_callback (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
-	struct request *rq;
-	
-	tape=&(drive->tape);
-	rq=HWGROUP(drive)->rq;
+	idetape_tape_t *tape=&(drive->tape);
+	struct request *rq=HWGROUP(drive)->rq;
+	int blocks_written=tape->pc->actually_transferred/tape->tape_block_size;
+		
 #if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_write_callback\n");
 #endif /* IDETAPE_DEBUG_LOG */
-	tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
-	if (!tape->pc->error) {
-#if IDETAPE_DEBUG_LOG
-		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG_LOG */
-		rq->sector+=rq->current_nr_sectors;
-		rq->nr_sectors-=rq->current_nr_sectors;
-		rq->current_nr_sectors=0;
+
+	tape->block_address+=blocks_written;
+	rq->current_nr_sectors-=blocks_written;
+
+	if (!tape->pc->error)
 		idetape_end_request (1,HWGROUP (drive));
-	}
 	else {
+		rq->errors=tape->pc->error;
 		idetape_end_request (0,HWGROUP (drive));
 	}
 	return;
@@ -2200,7 +2272,7 @@
 	pc->callback=&idetape_inquiry_callback;
 	pc->writing=0;
 	
-	idetape_zero_packet_command (pc);		
+	idetape_zero_packet_command (pc);
 	pc->c[0]=IDETAPE_INQUIRY_CMD;
 	pc->c[4]=255;
 }
@@ -2355,14 +2427,29 @@
 		if (result->filemark) {
 			pc->error=IDETAPE_RQ_ERROR_FILEMARK;
 			pc->abort=1;
-			return;
 		}
+	}
+
+	if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) {
 		if (result->sense_key == 8) {
 			pc->error=IDETAPE_RQ_ERROR_EOD;
 			pc->abort=1;
-			return;
 		}
 	}
+
+#if 1
+#ifdef CONFIG_BLK_DEV_TRITON
+
+	/*
+	 *	Correct pc->actually_transferred by asking the tape.
+	 */
+
+	if (pc->dma_error && pc->abort) {
+		unsigned long *long_ptr=(unsigned long *) &(result->information1);
+		pc->actually_transferred=pc->request_transfer-tape->tape_block_size*idetape_swap_long (*long_ptr);
+	}		
+#endif /* CONFIG_BLK_DEV_TRITON */
+#endif
 }
 
 void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc)
@@ -2546,12 +2633,16 @@
 	pc->writing=0;
 
 	idetape_zero_packet_command (pc);
+
 	pc->c [0]=IDETAPE_READ_CMD;
 	pc->c [1]=1;
 	pc->c [4]=original.b.b1;
 	pc->c [3]=original.b.b2;
 	pc->c [2]=original.b.b3;
 
+	if (length)
+		pc->dma_recommended=1;
+
 	return;
 }
 
@@ -2616,12 +2707,16 @@
 	pc->writing=1;
 
 	idetape_zero_packet_command (pc);
+
 	pc->c [0]=IDETAPE_WRITE_CMD;
 	pc->c [1]=1;
 	pc->c [4]=original.b.b1;
 	pc->c [3]=original.b.b2;
 	pc->c [2]=original.b.b3;
 
+	if (length)
+		pc->dma_recommended=1;
+
 	return;
 }
 
@@ -2699,7 +2794,6 @@
 			unsigned int cmd, unsigned long arg)
 {
 	idetape_packet_command_t pc;
-	int retval;
 	
 	pc.buffer=pc.temp_buffer;
 	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
@@ -2709,33 +2803,6 @@
 	printk ("ide-tape: Reached idetape_blkdev_ioctl\n");
 #endif /* IDETAPE_DEBUG_LOG */
 	switch (cmd) {
-		case IDETAPE_INQUIRY_IOCTL:
-#if IDETAPE_DEBUG_LOG
-			printk ("Adding INQUIRY packet command to the tail of the request queue\n");
-#endif /* IDETAPE_DEBUG_LOG */
-			idetape_create_inquiry_cmd (&pc);
-			pc.buffer=pc.temp_buffer;
-			pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
-			pc.current_position=pc.temp_buffer;
-			return (idetape_queue_pc_tail (drive,&pc));
-		case IDETAPE_LOCATE_IOCTL:
-#if IDETAPE_DEBUG_LOG
-			printk ("Adding LOCATE packet command to the tail of the request queue\n");
-#endif /* IDETAPE_DEBUG_LOG */
-			idetape_create_locate_cmd (&pc,arg,0);
-			retval=idetape_queue_pc_tail (drive,&pc);
-			if (retval!=0) return (retval);
-			
-			idetape_create_read_position_cmd (&pc);
-			pc.buffer=pc.temp_buffer;
-			pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
-			pc.current_position=pc.temp_buffer;
-			return (idetape_queue_pc_tail (drive,&pc));
-/*
-		case IDETAPE_RESET_IOCTL:
-			printk ("Resetting drive\n");
-			return (!ide_do_reset (drive));
-*/
 		default:
 			return -EIO;
 	}
@@ -2757,6 +2824,7 @@
 	idetape_tape_t *tape = &(drive->tape);
 	unsigned int major = HWIF(drive)->major;
 	struct blk_dev_struct *bdev = &blk_dev[major];
+	int error;
 
 #if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_end_request\n");
@@ -2766,7 +2834,8 @@
 
 	if (!rq->errors)				/* In case rq->errors is already set, */
 		rq->errors=!uptodate;			/* we won't change it. */
-	
+	error=rq->errors;
+		
 	if (tape->active_data_request == rq) {		/* The request was a pipelined data transfer request */
 
 		if (rq->cmd == IDETAPE_READ_REQUEST) {
@@ -2783,12 +2852,13 @@
 
 		if (rq->cmd == IDETAPE_WRITE_REQUEST) {
 			if (rq->errors)
-				tape->error_in_pipeline_stage=1;
+				tape->error_in_pipeline_stage=rq->errors;
 			idetape_remove_stage_head (drive);
 		}
 		
 		if (tape->next_stage == NULL) {
-			idetape_increase_max_pipeline_stages (drive);
+			if (!error)
+				idetape_increase_max_pipeline_stages (drive);
 			ide_end_drive_cmd (drive, 0, 0);
 			return;
 		}
@@ -2904,10 +2974,22 @@
 		 
 		 tape->postponed_rq = NULL;
 	}	
+
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);
+
+	/*
+	 *	After a software reset, the status register is locked. We
+	 *	will ignore the DSC value for our very first packet command,
+	 *	which will restore DSC operation.
+	 */
+
+	if (tape->reset_issued) {
+		status.b.dsc=1;
+		tape->reset_issued=0;
+	}
 	
 	switch (rq->cmd) {
 		case IDETAPE_READ_REQUEST:
-			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
 #if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing read request\n");
@@ -2930,7 +3012,6 @@
 			return;
 		
 		case IDETAPE_WRITE_REQUEST:
-			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
 #if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing write request\n");
@@ -2959,7 +3040,6 @@
  *	but I have occasionally missed DSC on a media access command otherwise.
  *	??? Still have to figure it out ...
  */
-			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffers are still not ready */
 #if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing packet command request\n");
@@ -3072,13 +3152,8 @@
 }
 
 /*
- *	idetape_queue_rw_tail is typically called from the character device
- *	interface to generate a read/write request for the block device interface
- *	and wait for it to be serviced. Note that cmd will be different than
- *	a buffer cache originated read/write request. This will be used
- *	in idetape_end_request.
- *
- *	Returns 0 on success or -EIO if an error occured.
+ *	idetape_queue_rw_tail generates a read/write request for the block
+ *	device interface and wait for it to be serviced.
  */
 
 int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer)
@@ -3090,16 +3165,21 @@
 #if IDETAPE_DEBUG_LOG
 	printk ("idetape_queue_rw_tail: cmd=%d\n",cmd);
 #endif /* IDETAPE_DEBUG_LOG */
-	/* build up a special read request, and add it to the queue */
-	
+#if IDETAPE_DEBUG_BUGS
+	if (tape->active_data_request != NULL) {
+		printk ("ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n");
+		return (0);
+	}
+#endif /* IDETAPE_DEBUG_BUGS */	
+
 	ide_init_drive_cmd (&rq);
 	rq.buffer = buffer;
 	rq.cmd = cmd;
 	rq.sector = tape->block_address;
-	rq.nr_sectors = blocks;
-	rq.current_nr_sectors = blocks;
-	tape->active_data_request=NULL;	/* Non-pipelined mode */
-	return ide_do_drive_cmd (drive, &rq, ide_wait);
+	rq.nr_sectors = rq.current_nr_sectors = blocks;
+	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
+
+	return (tape->tape_block_size*(blocks-rq.current_nr_sectors));
 }
 
 /*
@@ -3113,8 +3193,8 @@
 	idetape_tape_t *tape = &(drive->tape);
 	idetape_pipeline_stage_t *new_stage;
 	unsigned long flags;
-	struct request rq;
-	int errors;
+	struct request rq,*rq_ptr;
+	int bytes_read;
 	
 #if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_add_chrdev_read_request\n");
@@ -3123,8 +3203,7 @@
 	ide_init_drive_cmd (&rq);
 	rq.cmd = IDETAPE_READ_REQUEST;
 	rq.sector = tape->block_address;
-	rq.nr_sectors = blocks;
-	rq.current_nr_sectors = blocks;
+	rq.nr_sectors = rq.current_nr_sectors = blocks;
 
 	if (tape->current_number_of_stages < 0.5*tape->max_number_of_stages) {
 		new_stage=idetape_kmalloc_stage (drive);
@@ -3154,12 +3233,19 @@
 		idetape_wait_for_request (tape->active_data_request);
 	restore_flags (flags);
 
-	errors=tape->first_stage->rq.errors;
-	if (!errors)		
-		idetape_copy_buffer_from_stage (tape->first_stage,buffer);
-
-	idetape_remove_stage_head (drive);
-	return (errors ? -EIO:0);
+	rq_ptr=&(tape->first_stage->rq);
+	bytes_read=tape->tape_block_size*(rq_ptr->nr_sectors-rq_ptr->current_nr_sectors);
+	rq_ptr->nr_sectors=rq_ptr->current_nr_sectors=0;
+	idetape_copy_buffer_from_stage (tape->first_stage,buffer);
+	if (rq_ptr->errors != IDETAPE_RQ_ERROR_FILEMARK)
+		idetape_remove_stage_head (drive);
+#if IDETAPE_DEBUG_BUGS
+	if (bytes_read > blocks*tape->tape_block_size) {
+		printk ("ide-tape: bug: trying to return more bytes than requested\n");
+		bytes_read=blocks*tape->tape_block_size;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+	return (bytes_read);
 }
 
 /*
@@ -3186,8 +3272,6 @@
 	printk ("Reached idetape_add_chrdev_write_request\n");
 #endif /* IDETAPE_DEBUG_LOG */
 	
-	if (tape->error_in_pipeline_stage)		/* Return a deferred error */
-		return (-EIO);
 	
 	new_stage=idetape_kmalloc_stage (drive);
 
@@ -3242,10 +3326,15 @@
 	if (tape->active_data_request == NULL && tape->current_number_of_stages >= 0.75*tape->max_number_of_stages)
 		idetape_insert_pipeline_into_queue (drive);		
 
-	return (0);		
+	if (tape->error_in_pipeline_stage) {		/* Return a deferred error */
+		tape->error_in_pipeline_stage=0;
+		return (-EIO);
+	}
+	
+	return (blocks);
 }
 
-void idetape_empty_read_pipeline (ide_drive_t *drive)
+void idetape_discard_read_pipeline (ide_drive_t *drive)
 
 {
 	idetape_tape_t *tape = &(drive->tape);
@@ -3253,11 +3342,14 @@
 
 #if IDETAPE_DEBUG_BUGS
 	if (tape->chrdev_direction != idetape_direction_read) {
-		printk ("ide-tape: bug: Trying to empty read pipeline, but we are not reading.\n");
+		printk ("ide-tape: bug: Trying to discard read pipeline, but we are not reading.\n");
 		return;
 	}
 #endif /* IDETAPE_DEBUG_BUGS */
 
+	tape->merge_buffer_size=tape->merge_buffer_offset=0;
+	tape->chrdev_direction=idetape_direction_none;
+	
 	if (tape->first_stage == NULL)
 		return;
 		
@@ -3277,23 +3369,23 @@
 #endif /* IDETAPE_PIPELINE */
 }
 
-
-void idetape_empty_write_pipeline (ide_drive_t *drive)
+/*
+ *	idetape_wait_for_pipeline will wait until all pending pipeline
+ *	requests are serviced. Typically called on device close.
+ */
+ 
+void idetape_wait_for_pipeline (ide_drive_t *drive)
 
 {
 	idetape_tape_t *tape = &(drive->tape);
 	unsigned long flags;
 
-#if IDETAPE_DEBUG_BUGS
-	if (tape->chrdev_direction != idetape_direction_write) {
-		printk ("ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
-		return;
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-
 	if (tape->active_data_request == NULL)
 		idetape_insert_pipeline_into_queue (drive);		
 
+	if (tape->active_data_request == NULL)
+		return;
+
 	save_flags (flags);cli ();
 	if (tape->last_stage != NULL)
 		idetape_wait_for_request (&(tape->last_stage->rq));
@@ -3301,8 +3393,39 @@
 	else if (tape->active_data_request != NULL)
 		idetape_wait_for_request (tape->active_data_request);
 	restore_flags (flags);
+}
+
+void idetape_empty_write_pipeline (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+	int blocks;
 	
+#if IDETAPE_DEBUG_BUGS
+	if (tape->chrdev_direction != idetape_direction_write) {
+		printk ("ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
+		return;
+	}
+	if (tape->merge_buffer_size > tape->data_buffer_size) {
+		printk ("ide-tape: bug: merge_buffer too big\n");
+		tape->merge_buffer_size = tape->data_buffer_size;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+
+	if (tape->merge_buffer_size) {
+		blocks=tape->merge_buffer_size/tape->tape_block_size;
+		if (tape->merge_buffer_size % tape->tape_block_size) {
+			blocks++;
+			memset (tape->merge_buffer+tape->merge_buffer_size,0,tape->data_buffer_size-tape->merge_buffer_size);
+		}
+		(void) idetape_add_chrdev_write_request (drive,blocks,tape->merge_buffer);
+		tape->merge_buffer_size=0;
+	}
+	
+	idetape_wait_for_pipeline (drive);
+
 	tape->error_in_pipeline_stage=0;
+	tape->chrdev_direction=idetape_direction_none;
 
 	/*
 	 *	On the next backup, perform the feedback loop again.
@@ -3316,7 +3439,6 @@
 #else
 	tape->max_number_of_stages=0;
 #endif /* IDETAPE_PIPELINE */
-
 #if IDETAPE_DEBUG_BUGS
 	if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->current_number_of_stages != 0) {
 		printk ("ide-tape: ide-tape pipeline bug\n");		
@@ -3324,7 +3446,6 @@
 #endif /* IDETAPE_DEBUG_BUGS */
 }
 
-
 /*
  *	idetape_zero_packet_command just zeros a packet command and
  *	sets the number of retries to 0, as we haven't retried it yet.
@@ -3339,6 +3460,8 @@
 		pc->c[i]=0;
 	pc->retries=0;
 	pc->abort=0;
+	pc->dma_recommended=0;
+	pc->dma_error=0;
 }
 
 /*
@@ -3494,27 +3617,20 @@
  *	Our character device read / write functions.
  *
  *	The tape is optimized to maximize throughput when it is transfering
- *	an integral number of the "continous transfer limit", which is
- *	a parameter of the specific tape (26 KB on my particular tape). The
- *	resulting increase in performance should be dramatical. In the
- *	character device read/write functions, we split the current
- *	request to units of the above size, and handle the remaining bytes
- *	in some other sub-functions.
- *
- *	In case the count number is not even an integral number of the tape
- *	block size (usually 512 or 1024 bytes), we will pad the transfer with
- *	zeroes (write) or read the entire block and return only the requested
- *	bytes (but the tape will be in the "wrong" position). Do not supply
- *	such a count value unless you are going to close the device right
- *	after this request.
+ *	an integral number of the "continuous transfer limit", which is
+ *	a parameter of the specific tape (26 KB on my particular tape).
  *
- *	Again, for best results use an integral number of the tape's parameter
+ *	For best results use an integral number of the tape's parameter
  *	(which is displayed in the driver installation stage and is returned
  *	 by the MTIOCGET ioctl).
- */
-
-/*
- *	Our character device read function.
+ *
+ *	As of version 1.3 of the driver, the character device provides an
+ *	abstract continuous view of the media - any mix of block sizes (even 1
+ *	byte) on the same backup/restore procedure is supported. The driver
+ *	will internally convert the requests to the recommended transfer unit,
+ *	so that an unmatch between the user's block size to the recommended
+ *	size will only result in a (slightly) increased driver overhead, but
+ *	will no longer hit performance.
  */
 
 int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count)
@@ -3522,9 +3638,8 @@
 {
 	ide_drive_t *drive=idetape_chrdev.drive;
 	idetape_tape_t *tape=&(drive->tape);
-	int blocks,remainder,retval;
-	char *buf_ptr;
-	unsigned long previous_block_address,actually_read;
+	char *buf_ptr=buf;
+	int bytes_read,temp,actually_read=0;
 
 #if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_read\n");
@@ -3542,9 +3657,9 @@
 		 *	mode.
 		 */
 		 
-		retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,0,tape->temp_data_buffer);
-		if (retval)
-			return (retval);
+		bytes_read=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,0,tape->merge_buffer);
+		if (bytes_read < 0)
+			return (bytes_read);
 
 		tape->chrdev_direction=idetape_direction_read;
 	}
@@ -3552,97 +3667,55 @@
 	if (count==0)
 		return (0);
 
-	actually_read=0;
-	buf_ptr=buf;
-	blocks=count/tape->data_buffer_size;
-	remainder=count%tape->data_buffer_size;
-	
-	while (blocks) {
-		previous_block_address=tape->block_address;
-		retval=idetape_add_chrdev_read_request (drive,tape->capabilities.ctl,tape->temp_data_buffer);
-		if (tape->max_number_of_stages)
-			actually_read+=tape->data_buffer_size;
-		else
-			actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
-
-		if (retval) {
-			if (tape->max_number_of_stages)
-				return (0);
-			else
-				return (actually_read);
+	if (tape->merge_buffer_size) {
+#if IDETAPE_DEBUG_BUGS
+		if (tape->merge_buffer_offset+tape->merge_buffer_size > tape->data_buffer_size) {
+			printk ("ide-tape: bug: merge buffer too big\n");
+			tape->merge_buffer_offset=0;tape->merge_buffer_size=tape->data_buffer_size-1;
 		}
-		memcpy_tofs (buf_ptr,tape->temp_data_buffer,tape->data_buffer_size);
-		buf_ptr+=tape->data_buffer_size;
-		blocks--;
+#endif /* IDETAPE_DEBUG_BUGS */
+		actually_read=IDETAPE_MIN (tape->merge_buffer_size,count);
+		memcpy_tofs (buf_ptr,tape->merge_buffer+tape->merge_buffer_offset,actually_read);
+		buf_ptr+=actually_read;tape->merge_buffer_size-=actually_read;
+		count-=actually_read;tape->merge_buffer_offset+=actually_read;
+	}
+
+	while (count >= tape->data_buffer_size) {
+		bytes_read=idetape_add_chrdev_read_request (drive,tape->capabilities.ctl,tape->merge_buffer);
+		if (bytes_read <= 0)
+			return (actually_read);
+		memcpy_tofs (buf_ptr,tape->merge_buffer,bytes_read);
+		buf_ptr+=bytes_read;count-=bytes_read;actually_read+=bytes_read;
+	}
+
+	if (count) {
+		bytes_read=idetape_add_chrdev_read_request (drive,tape->capabilities.ctl,tape->merge_buffer);
+		if (bytes_read <= 0)
+			return (actually_read);
+		temp=IDETAPE_MIN (count,bytes_read);
+		memcpy_tofs (buf_ptr,tape->merge_buffer,temp);
+		actually_read+=temp;
+		tape->merge_buffer_offset=temp;
+		tape->merge_buffer_size=bytes_read-temp;
 	}
-	if (remainder)
-		return (actually_read+idetape_chrdev_read_remainder (inode,file,buf_ptr,remainder));
-	else
-		return (actually_read);		
+	return (actually_read);
 }
  
-int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count)
+int idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, int count)
 
 {
 	ide_drive_t *drive=idetape_chrdev.drive;
 	idetape_tape_t *tape=&(drive->tape);
-	int blocks,remainder,retval;
-	unsigned long previous_block_address,actually_read;
-
-#if IDETAPE_DEBUG_LOG
-	printk ("Reached idetape_chrdev_read_remainder\n");
-#endif /* IDETAPE_DEBUG_LOG */
-
-	blocks=count/tape->tape_block_size;
-	remainder=count%tape->tape_block_size;
-	if (remainder) {
-#if IDETAPE_DEBUG_LOG
-	printk ("ide-tape: Padding read to block boundary\n");
-#endif /* IDETAPE_DEBUG_LOG */
-		blocks++;
-	}
-#if IDETAPE_DEBUG_LOG
-	printk ("Adding a READ request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG_LOG */
-	previous_block_address=tape->block_address;
-	retval=idetape_add_chrdev_read_request (drive,blocks,tape->temp_data_buffer);
-	if (retval) {
-		if (tape->max_number_of_stages)
-			actually_read=0;
-		else
-			actually_read=tape->tape_block_size*(tape->block_address-previous_block_address);
-		if (actually_read > count)
-			actually_read=count;
-		if (actually_read != 0)
-			memcpy_tofs (buf,tape->temp_data_buffer,actually_read);
-		return (actually_read);
-	}
-#if IDETAPE_DEBUG_LOG
-	printk ("Copying %d bytes to the user space memory\n",count);
-#endif /* IDETAPE_DEBUG_LOG */
-	memcpy_tofs (buf,tape->temp_data_buffer,count);
-	return (count);
-}
-
-int idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, int count)
-
-{
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
-	int blocks,remainder,retval;
-	const char *buf_ptr;
-	unsigned long previous_block_address,actually_written;
+	const char *buf_ptr=buf;
+	int retval,actually_written=0;
 
 #if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_write\n");
 #endif /* IDETAPE_DEBUG_LOG */
 
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-
 	if (tape->chrdev_direction != idetape_direction_write) {	/* Initialize write operation */
 		if (tape->chrdev_direction == idetape_direction_read)
-			idetape_empty_read_pipeline (drive);
+			idetape_discard_read_pipeline (drive);
 
 		/*
 		 *	Issue a write 0 command to ensure that DSC handshake
@@ -3650,93 +3723,51 @@
 		 *	mode.
 		 */
 
-		retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,0,tape->temp_data_buffer);
-		if (retval)
+		retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,0,tape->merge_buffer);
+		if (retval < 0)
 			return (retval);		
-	
+
 		tape->chrdev_direction=idetape_direction_write;
 	}
 
 	if (count==0)
 		return (0);
 
-	actually_written=0;
-	buf_ptr=buf;
-	blocks=count/tape->data_buffer_size;
-	remainder=count%tape->data_buffer_size;
-
-	while (blocks) {
-		memcpy_fromfs (tape->temp_data_buffer,buf_ptr,tape->data_buffer_size);
-		buf_ptr+=tape->data_buffer_size;
-		previous_block_address=tape->block_address;
-		retval=idetape_add_chrdev_write_request (drive,tape->capabilities.ctl,tape->temp_data_buffer);
-		if (tape->max_number_of_stages)
-			actually_written+=tape->data_buffer_size;	/* Pipelined mode - Cheat :-) */
-		else
-			actually_written+=tape->tape_block_size*(tape->block_address-previous_block_address);
+	if (tape->merge_buffer_size) {
+#if IDETAPE_DEBUG_BUGS
+		if (tape->merge_buffer_size >= tape->data_buffer_size) {
+			printk ("ide-tape: bug: merge buffer too big\n");
+			tape->merge_buffer_size=0;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
 
-		if (retval) {
-			if (tape->max_number_of_stages)
-				return (0);
-			else
-				return (actually_written);
+		actually_written=IDETAPE_MIN (tape->data_buffer_size-tape->merge_buffer_size,count);
+		memcpy_fromfs (tape->merge_buffer+tape->merge_buffer_size,buf_ptr,actually_written);
+		buf_ptr+=actually_written;tape->merge_buffer_size+=actually_written;count-=actually_written;
+
+		if (tape->merge_buffer_size == tape->data_buffer_size) {
+			tape->merge_buffer_size=0;
+			retval=idetape_add_chrdev_write_request (drive,tape->capabilities.ctl,tape->merge_buffer);
+			if (retval <= 0)
+				return (retval);
 		}
-		blocks--;
 	}
-	if (remainder)
-		return (actually_written+idetape_chrdev_write_remainder (inode,file,buf_ptr,remainder));
-	else
-		return (actually_written);		
-}
-
-int idetape_chrdev_write_remainder (struct inode *inode, struct file *file, const char *buf, int count)
-
-{
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
-	int blocks,remainder,retval;
-	char *ptr;
-	unsigned long previous_block_address,actually_written;
-
-#if IDETAPE_DEBUG_LOG
-	printk ("Reached idetape_chrdev_write_remainder\n");
-#endif /* IDETAPE_DEBUG_LOG */
-		
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-
-	blocks=count/tape->tape_block_size;
-	remainder=count%tape->tape_block_size;
-	if (remainder)
-		blocks++;
-#if IDETAPE_DEBUG_LOG
-	printk ("Copying %d bytes from the user space memory\n",count);
-#endif /* IDETAPE_DEBUG_LOG */
 
-	memcpy_fromfs (tape->temp_data_buffer,buf,count);
-	if (remainder) {
-#if IDETAPE_DEBUG_LOG
-	printk ("ide-tape: Padding written data to block boundary\n");
-#endif /* IDETAPE_DEBUG_LOG */
-		ptr=tape->temp_data_buffer+(blocks-1)*tape->tape_block_size;
-		memset (ptr,0,remainder);
+	while (count >= tape->data_buffer_size) {
+		memcpy_fromfs (tape->merge_buffer,buf_ptr,tape->data_buffer_size);
+		buf_ptr+=tape->data_buffer_size;count-=tape->data_buffer_size;
+		retval=idetape_add_chrdev_write_request (drive,tape->capabilities.ctl,tape->merge_buffer);
+		actually_written+=tape->data_buffer_size;
+		if (retval <= 0)
+			return (retval);
 	}
-#if IDETAPE_DEBUG_LOG
-	printk ("Adding a WRITE request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG_LOG */
 
-	previous_block_address=tape->block_address;
-	retval=idetape_add_chrdev_write_request (drive,blocks,tape->temp_data_buffer);
-	if (retval) {
-		if (tape->max_number_of_stages)
-			actually_written=0;
-		else
-			actually_written=tape->tape_block_size*(tape->block_address-previous_block_address);
-		if (actually_written > count)
-			actually_written=count;
-		return (actually_written);
+	if (count) {
+		actually_written+=count;
+		memcpy_fromfs (tape->merge_buffer,buf_ptr,count);
+		tape->merge_buffer_size+=count;
 	}
-	return (count);
+	return (actually_written);
 }
 
 /*
@@ -3788,14 +3819,10 @@
 	if (tape->chrdev_direction == idetape_direction_write) {
 		idetape_empty_write_pipeline (drive);
 		idetape_flush_tape_buffers (drive);
-		tape->chrdev_direction=idetape_direction_none;
 	}
 
-	if (tape->chrdev_direction == idetape_direction_read)
-		if (cmd != MTIOCTOP) {
-			idetape_empty_read_pipeline (drive);
-			tape->chrdev_direction=idetape_direction_none;
-		}
+	if (tape->chrdev_direction == idetape_direction_read && cmd != MTIOCTOP)
+		idetape_discard_read_pipeline (drive);
 	
 	pc.buffer=pc.temp_buffer;
 	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
@@ -3904,6 +3931,8 @@
 		case MTFSFM:
 		case MTBSF:
 		case MTBSFM:
+			if (!mt_count)
+				return (0);
 			return (idetape_space_over_filemarks (drive,mt_op,mt_count));
 		default:
 			break;
@@ -3913,10 +3942,8 @@
 	 *	Empty the pipeline.
 	 */
 
-	if (tape->chrdev_direction == idetape_direction_read) {
-		idetape_empty_read_pipeline (drive);
-		tape->chrdev_direction=idetape_direction_none;
-	}
+	if (tape->chrdev_direction == idetape_direction_read)
+		idetape_discard_read_pipeline (drive);
 
 	switch (mt_op) {
 		case MTWEOF:
@@ -3976,7 +4003,8 @@
 		 *	We have a read-ahead buffer. Scan it for crossed
 		 *	filemarks.
 		 */
-		 
+
+		tape->merge_buffer_size=tape->merge_buffer_offset=0;
 		while (tape->first_stage != NULL) {
 			
 			/*
@@ -4003,7 +4031,7 @@
 			}
 			idetape_remove_stage_head (drive);
 		}
-		tape->chrdev_direction = idetape_direction_none;
+		idetape_discard_read_pipeline (drive);
 	}
 
 	/*
@@ -4049,10 +4077,10 @@
 int idetape_chrdev_open (struct inode *inode, struct file *filp)
 
 {
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
 	unsigned long flags;
-	unsigned int minor;
+	unsigned int minor=MINOR (inode->i_rdev),allocation_length;
 			
 	save_flags (flags);cli ();
 
@@ -4060,11 +4088,6 @@
 	printk ("Reached idetape_chrdev_open\n");
 #endif /* IDETAPE_DEBUG_LOG */
 
-
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-	minor=MINOR (inode->i_rdev);
-
 	if (minor!=0 && minor!=128) {		/* Currently supporting only one */
 		restore_flags (flags);		/* tape drive */
 		return (-ENXIO);
@@ -4078,6 +4101,26 @@
 	tape->busy=1;
 	restore_flags (flags);
 
+	allocation_length=tape->data_buffer_size;
+	if (tape->data_buffer_size % IDETAPE_ALLOCATION_BLOCK)
+		allocation_length+=IDETAPE_ALLOCATION_BLOCK;
+
+#if IDETAPE_MINIMIZE_IDLE_MEMORY_USAGE
+	if (tape->data_buffer == NULL)
+		tape->data_buffer=kmalloc (allocation_length,GFP_KERNEL);
+	if (tape->data_buffer == NULL)
+		goto sorry;
+	if (tape->merge_buffer == NULL)
+		tape->merge_buffer=kmalloc (allocation_length,GFP_KERNEL);
+	if (tape->merge_buffer == NULL) {
+		kfree (tape->data_buffer);
+	sorry:
+		printk ("ide-tape: FATAL - Can not allocate continuous buffer of %d bytes\n",allocation_length);
+		tape->busy=0;
+		return (-EIO);
+	}
+#endif /* IDETAPE_MINIMIZE_IDLE_MEMORY_USAGE */
+
 	if (!tape->block_address_valid) {
 		if (idetape_rewind_tape (drive)) {
 			printk ("ide-tape: Rewinding tape failed\n");
@@ -4096,10 +4139,9 @@
 void idetape_chrdev_release (struct inode *inode, struct file *filp)
 
 {
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
-	
-	unsigned int minor;
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
+	unsigned int minor=MINOR (inode->i_rdev);
 	idetape_packet_command_t pc;
 	unsigned long flags;
 			
@@ -4107,28 +4149,32 @@
 	printk ("Reached idetape_chrdev_release\n");
 #endif /* IDETAPE_DEBUG_LOG */
 
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-	minor=MINOR (inode->i_rdev);
-
 	if (tape->chrdev_direction == idetape_direction_write) {
 		idetape_empty_write_pipeline (drive);
-		tape->chrdev_direction = idetape_direction_none;
 		idetape_create_write_filemark_cmd (&pc,1);	/* Write a filemark */
-		if (idetape_queue_pc_tail (drive,&pc)) {
+		if (idetape_queue_pc_tail (drive,&pc))
 			printk ("ide-tape: Couldn't write a filemark\n");
-			/* ??? */
-		}
 	}
-
-	if (minor < 128) {
-		if (tape->chrdev_direction == idetape_direction_read)
-			idetape_empty_read_pipeline (drive);
-		if (idetape_rewind_tape (drive)) {
+	
+	if (tape->chrdev_direction == idetape_direction_read) {
+		if (minor < 128)
+			idetape_discard_read_pipeline (drive);
+		else
+			idetape_wait_for_pipeline (drive);
+	}
+	
+	if (minor < 128)
+		if (idetape_rewind_tape (drive))
 			printk ("ide-tape: Rewinding tape failed\n");
-			/* ??? */
-		}
+
+#if IDETAPE_MINIMIZE_IDLE_MEMORY_USAGE
+	kfree (tape->data_buffer);
+	tape->data_buffer=NULL;
+	if (!tape->merge_buffer_size) {
+		kfree (tape->merge_buffer);
+		tape->merge_buffer=NULL;
 	}
+#endif /* IDETAPE_MINIMIZE_IDLE_MEMORY_USAGE */
 
 	save_flags (flags);cli ();
 	tape->busy=0;
@@ -4307,7 +4353,7 @@
 
 /*
  *	idetape_copy_buffer_from_stage and idetape_copy_buffer_to_stage
- *	copy data from/to the small buffers into/from a continous buffer.
+ *	copy data from/to the small buffers into/from a continuous buffer.
  */
   
 void idetape_copy_buffer_from_stage (idetape_pipeline_stage_t *stage,char *buffer)

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this