patch-1.3.46 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.45/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
@@ -0,0 +1,3313 @@
+/*
+ * linux/drivers/block/ide-tape.c	Version 1.0 - ALPHA	Dec  3, 1995
+ *
+ * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ *
+ * This driver was constructed as a student project in the software laboratory
+ * of the faculty of electrical engineering in the Technion - Israel's
+ * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David.
+ *
+ * It is hereby placed under the terms of the GNU general public license.
+ * (See linux/COPYING).
+ */
+ 
+/*
+ * IDE ATAPI streaming tape driver.
+ *
+ * This driver is a part of the Linux ide driver and works in co-operation
+ * with linux/drivers/block/ide.c.
+ *
+ * This driver provides both a block device and a character device interface to
+ * the tape. The driver, in co-operation with ide.c, basically traverses the
+ * request-list for the block device interface. The character device interface,
+ * on the other hand, creates new requests, adds them to the request-list
+ * of the block device, and waits for their completion.
+ *
+ * The block device major and minor numbers are determined from the
+ * tape relative position in the ide interfaces, as explained in ide.c.
+ *
+ * The character device interface consists of two devices:
+ *
+ * ht0		major=37,minor=0	first IDE tape, rewind on close.
+ * nht0		major=37,minor=128	first IDE tape, no rewind on close.
+ *
+ * Run /usr/src/linux/drivers/block/MAKEDEV.ide to create the above entries.
+ * We currently support only one ide tape drive.
+ *
+ * Although we do support requests which originate from the buffer cache to
+ * some extent, it is recommended to use the character device interface when
+ * performing a long read or write operation (relative to the amount of free
+ * memory in your system). Otherwise, free memory will be used to cache tape
+ * blocks, those cached blocks won't be used, Linux's responsiveness will
+ * suffer as we start to swap.
+ *
+ * The general magnetic tape commands compatible interface, as defined by
+ * include/linux/mtio.h, is accessible through the character device.
+ * Our own ide-tape ioctl's can can be issued to either the block device or
+ * the character device.
+ *
+ * Opening the block device interface will be refused by default.
+ *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ *
+ * Ver 0.1   Nov  1 95   Pre-working code :-)
+ * Ver 0.2   Nov 23 95   A short backup (few megabytes) and restore procedure
+ *                        was successful ! (Using tar cvf ... on the block
+ *                        device interface).
+ *                       A longer backup resulted in major swapping, bad
+ *                        overall Linux performance and eventually failed as
+ *                        we received non serial read-ahead requests from the
+ *                        buffer cache.
+ * Ver 0.3   Nov 28 95   Long backups are now possible, thanks to the
+ *                        character device interface. Linux's responsiveness
+ *                        and performance doesn't seem to be much affected
+ *                        from the background backup procedure.
+ *                       Some general mtio.h magnetic tape operations are
+ *                        now supported by our character device. As a result,
+ *                        popular tape utilities are starting to work with
+ *                        ide tapes :-)
+ *                       The following configurations were tested:
+ *                       	1. An IDE ATAPI TAPE shares the same interface
+ *                       	   and irq with an IDE ATAPI CDROM.
+ *                        	2. An IDE ATAPI TAPE shares the same interface
+ *                          	   and irq with a normal IDE disk.
+ *                        Both configurations seemed to work just fine !
+ *                        However, to be on the safe side, it is meanwhile
+ *                        recommended to give the IDE TAPE its own interface
+ *                        and irq.
+ *                       The one thing which needs to be done here is to
+ *                        add a "request postpone" feature to ide.c,
+ *                        so that we won't have to wait for the tape to finish
+ *                        performing a long media access (DSC) request (such
+ *                        as a rewind) before we can access the other device
+ *                        on the same interface. This effect doesn't disturb
+ *                        normal operation most of the time because read/write
+ *                        requests are relatively fast, and once we are
+ *                        performing one tape r/w request, a lot of requests
+ *                        from the other device can be queued and ide.c will
+ *			  service all of them after this single tape request.
+ * Ver 1.0   ???         Integrated into Linux 1.3.??? development tree.
+ *                       On each read / write request, we now ask the drive
+ *                        if we can transfer a constant number of bytes
+ *                        (a parameter of the drive) only to its buffers,
+ *                        without causing actual media access. If we can't,
+ *                        we just wait until we can by polling the DSC bit.
+ *                        This ensures that while we are not transferring
+ *                        more bytes than the constant reffered to above, the
+ *                        interrupt latency will not become too high and
+ *                        we won't cause an interrupt timeout, as happened
+ *                        occasionally in the previous version.
+ *                       While polling for DSC, the current request is
+ *                        postponed and ide.c is free to handle requests from
+ *                        the other device. This is handled transparently to
+ *                        ide.c. The hwgroup locking method which was used
+ *                        in the previous version was removed.
+ *                       Use of new general features which are provided by
+ *                        ide.c for use with atapi devices.
+ *                        (Programming done by Mark Lord)
+ *                       Few potential bug fixes (Again, suggested by Mark)
+ *                       Single character device data transfers are now
+ *                        not limited in size, as they were before.
+ *                       We are asking the tape about its recommended
+ *                        transfer unit and send a larger data transfer
+ *                        as several transfers of the above size.
+ *                        For best results, use an integral number of this
+ *                        basic unit (which is shown during driver
+ *                        initialization). I will soon add an ioctl to get
+ *                        this important parameter.
+ *                       Our data transfer buffer is allocated on startup,
+ *                        rather than before each data transfer. This should
+ *                        ensure that we will indeed have a data buffer.
+ *
+ * We are currently in an *alpha* stage. The driver is not complete and not
+ * much tested. I would strongly suggest to:
+ *
+ *	1. Connect the tape to a separate interface and irq.
+ *	2. Be truly prepared for a kernel crash and the resulting data loss.
+ *	3. Don't rely too much on the resulting backups.
+ *
+ * Other than that, enjoy !
+ *
+ * Here are some words from the first releases of hd.c, which are quoted
+ * in ide.c and apply here as well:
+ *
+ * | Special care is recommended.  Have Fun!
+ *
+ */
+
+#include <linux/hdreg.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#define _IDE_TAPE_C			/* For ide_end_request in blk.h */
+
+/*
+ *	Main Linux ide driver include file
+ *
+ *	Automatically includes our first include file - ide-tape1.h.
+ */
+ 
+#include "ide.h"		
+
+/*
+ *	Supported ATAPI tape drives packet commands
+ */
+
+#define	IDETAPE_TEST_UNIT_READY_CMD	0x00
+#define	IDETAPE_REWIND_CMD		0x01
+#define	IDETAPE_REQUEST_SENSE_CMD	0x03
+#define	IDETAPE_READ_CMD		0x08
+#define	IDETAPE_WRITE_CMD		0x0a
+#define	IDETAPE_WRITE_FILEMARK_CMD	0x10
+#define	IDETAPE_SPACE_CMD		0x11
+#define	IDETAPE_INQUIRY_CMD		0x12
+#define	IDETAPE_ERASE_CMD		0x19
+#define	IDETAPE_MODE_SENSE_CMD		0x1a
+#define	IDETAPE_LOCATE_CMD		0x2b
+#define	IDETAPE_READ_POSITION_CMD	0x34
+
+/*
+ *	Some defines for the SPACE command
+ *
+ *	(The code field in the SPACE packet command).
+ */
+ 
+#define	IDETAPE_SPACE_OVER_FILEMARK	1
+#define	IDETAPE_SPACE_TO_EOD		3
+
+/*
+ *	Our ioctls - We will use 0x034n and 0x035n
+ *
+ *	Nothing special meanwhile.
+ *	mtio.h MTIOCTOP compatible commands are supported on the character
+ *	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.
+ *
+ *	In order to service a character device command, we add special
+ *	requests to the tail of our block device request queue and wait
+ *	for their completion.
+ *
+ */
+
+/*
+ * 	IDETAPE_PACKET_COMMAND_REQUEST_TYPE1 is used to queue a packet command
+ *	in the request queue. We will wait for DSC before issuing the command
+ *	if it is still not set. In that case, we will temporary replace the
+ *	cmd field to type 2 and restore it back to type 1 when we receive DSC
+ *	and can start with sending the command.
+ */
+ 
+#define	IDETAPE_PACKET_COMMAND_REQUEST_TYPE1	90
+#define	IDETAPE_PACKET_COMMAND_REQUEST_TYPE2	91
+
+/*
+ *	IDETAPE_READ_REQUEST and IDETAPE_WRITE_REQUEST are used by our
+ *	character device interface to request read/write operations from
+ *	our block device interface.
+ *
+ *	In case a read or write request was requested by the buffer cache
+ *	and not by our character device interface, the cmd field in the
+ *	request will contain READ and WRITE instead.
+ *
+ *	We handle both cases in a similar way. The main difference is that
+ *	in our own requests, buffer head is NULL and idetape_end_request
+ *	will update the errors field if the request was not completed.
+ */
+
+#define	IDETAPE_READ_REQUEST			92
+#define	IDETAPE_WRITE_REQUEST			93
+
+/*
+ *	We are now able to postpone an idetape request in the stage
+ *	where it is polling for DSC and service requests from the other
+ *	ide device meanwhile.
+ */
+
+#define	IDETAPE_RQ_POSTPONED		0x1234
+
+/*
+ *	ATAPI Task File Registers (Re-definition of the ATA Task File
+ *	Registers for an ATAPI packet command).
+ * 	From Table 3-2 of QIC-157C.
+ */
+
+/* Read Access */
+
+#define	IDETAPE_DATA_OFFSET		(0)
+#define IDETAPE_ERROR_OFFSET		(1)
+#define	IDETAPE_IREASON_OFFSET		(2)
+#define IDETAPE_RESERVED3_OFFSET	(3)
+#define IDETAPE_BCOUNTL_OFFSET		(4)
+#define	IDETAPE_BCOUNTH_OFFSET		(5)
+#define IDETAPE_DRIVESEL_OFFSET		(6)
+#define	IDETAPE_STATUS_OFFSET		(7)
+
+#define	IDETAPE_DATA_REG		(HWIF(drive)->io_base+IDETAPE_DATA_OFFSET)
+#define IDETAPE_ERROR_REG		(HWIF(drive)->io_base+IDETAPE_ERROR_OFFSET)
+#define	IDETAPE_IREASON_REG		(HWIF(drive)->io_base+IDETAPE_IREASON_OFFSET)
+#define IDETAPE_RESERVED3_REG		(HWIF(drive)->io_base+IDETAPE_RESERVED3_OFFSET)
+#define IDETAPE_BCOUNTL_REG		(HWIF(drive)->io_base+IDETAPE_BCOUNTL_OFFSET)
+#define	IDETAPE_BCOUNTH_REG		(HWIF(drive)->io_base+IDETAPE_BCOUNTH_OFFSET)
+#define IDETAPE_DRIVESEL_REG		(HWIF(drive)->io_base+IDETAPE_DRIVESEL_OFFSET)
+#define	IDETAPE_STATUS_REG		(HWIF(drive)->io_base+IDETAPE_STATUS_OFFSET)
+
+/* Write Access */
+
+#define	IDETAPE_FEATURES_OFFSET		(1)
+#define IDETAPE_ATACOMMAND_OFFSET	(7)
+
+#define IDETAPE_FEATURES_REG		(HWIF(drive)->io_base+IDETAPE_FEATURES_OFFSET)
+#define IDETAPE_ATACOMMAND_REG		(HWIF(drive)->io_base+IDETAPE_ATACOMMAND_OFFSET)
+#define IDETAPE_CONTROL_REG		(HWIF(drive)->ctl_port)
+
+
+/*
+ *	Structure of the various task file registers
+ */
+
+/*
+ *	The ATAPI Status Register.
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned check		:1;	/* Error occured */
+		unsigned idx		:1;	/* Reserved */
+		unsigned corr		:1;	/* Correctable error occured */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned dsc		:1;	/* Set when a media access command is finished */
+						/* Reads / Writes are NOT media access commands */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands */
+						/* (The device is ready to accept ATA command) */
+		unsigned bsy		:1;	/* The device has access to the command block */
+	} b;
+} idetape_status_reg_t;
+
+/*
+ *	The ATAPI error register.
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned ili		:1;	/* Illegal Length Indication */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+	} b;
+} idetape_error_reg_t;
+
+/*
+ *	ATAPI Feature Register
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned dma		:1;	/* Using DMA of PIO */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved7	:1;	/* Reserved */
+	} b;
+} idetape_feature_reg_t;
+
+/*
+ *	ATAPI Byte Count Register.
+ */
+ 
+typedef union {
+	unsigned all			:16;
+	struct {
+		unsigned low		:8;	/* LSB */
+		unsigned high		:8;	/* MSB */
+	} b;
+} idetape_bcount_reg_t;
+
+/*
+ *	ATAPI Interrupt Reason Register.
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned reserved	:6;	/* Reserved */
+	} b;
+} idetape_ireason_reg_t;
+
+/*
+ *	ATAPI Drive Select Register
+ */
+ 
+typedef union {	
+	unsigned all			:8;
+	struct {
+		unsigned sam_lun	:4;	/* Should be zero with ATAPI (not used) */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one7		:1;	/* Should be set to 1 */
+	} b;
+} idetape_drivesel_reg_t;
+
+/*
+ *	ATAPI Device Control Register
+ */
+ 
+typedef union {			
+	unsigned all			:8;
+	struct {
+		unsigned zero0		:1;	/* Should be set to zero */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned reserved4567	:4;	/* Reserved */
+	} b;
+} idetape_control_reg_t;
+
+/*
+ *	idetape_chrdev_t provides the link between out character device
+ *	interface and our block device interface and the corresponding
+ *	ide_drive_t structure.
+ *
+ *	We currently support only one tape drive.
+ * 
+ */
+ 
+typedef struct {
+	ide_drive_t *drive;
+	int major,minor;
+	char name[4];
+} idetape_chrdev_t;
+
+/*
+ *	The following is used to format the general configuration word of
+ *	the ATAPI IDENTIFY DEVICE command.
+ */
+
+struct idetape_id_gcw {	
+
+	unsigned packet_size	:2;	/* Packet Size */
+	unsigned reserved2	:1;	/* Reserved */
+	unsigned reserved3	:1;	/* Reserved */
+	unsigned reserved4	:1;	/* Reserved */
+	unsigned drq_type	:2;	/* Command packet DRQ type */
+	unsigned removable	:1;	/* Removable media */
+	unsigned device_type	:5;	/* Device type */
+	unsigned reserved13	:1;	/* Reserved */
+	unsigned protocol	:2;	/* Protocol type */
+};
+
+/*
+ *	INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+ 
+typedef struct {
+	unsigned device_type	:5;	/* Peripheral Device Type */
+	unsigned reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned reserved1_6t0	:7;	/* Reserved */
+	unsigned rmb		:1;	/* Removable Medium Bit */
+	unsigned ansi_version	:3;	/* ANSI Version */
+	unsigned ecma_version	:3;	/* ECMA Version */
+	unsigned iso_version	:2;	/* ISO Version */
+	unsigned response_format :4;	/* Response Data Format */
+	unsigned reserved3_45	:2;	/* Reserved */
+	unsigned reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned reserved3_7	:1;	/* AENC - Reserved */
+	byte additional_length;		/* Additional Length (total_length-4) */
+	byte reserved_5;		/* Reserved */
+	byte reserved_6;		/* Reserved */
+	unsigned reserved7_0	:1;	/* SftRe - Reserved */
+	unsigned reserved7_1	:1;	/* CmdQue - Reserved */
+	unsigned reserved7_2	:1;	/* Reserved */
+	unsigned reserved7_3	:1;	/* Linked - Reserved */
+	unsigned reserved7_4	:1;	/* Sync - Reserved */
+	unsigned reserved7_5	:1;	/* WBus16 - Reserved */
+	unsigned reserved7_6	:1;	/* WBus32 - Reserved */
+	unsigned reserved7_7	:1;	/* RelAdr - Reserved */
+	byte vendor_id [8];		/* Vendor Identification */
+	byte product_id [16];		/* Product Identification */
+	byte revision_level [4];	/* Revision Level */
+	byte vendor_specific [20];	/* Vendor Specific - Optional */
+	byte reserved56t95 [40];	/* Reserved - Optional */
+	
+					/* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ *	READ POSITION packet command - Data Format (From Table 6-57)
+ */
+ 
+typedef struct {
+	unsigned reserved0_10	:2;	/* Reserved */
+	unsigned bpu		:1;	/* Block Position Unknown */	
+	unsigned reserved0_543	:3;	/* Reserved */
+	unsigned eop		:1;	/* End Of Partition */
+	unsigned bop		:1;	/* Begining Of Partition */
+	byte partition_num;		/* Partition Number */
+	byte reserved_2;		/* Reserved */
+	byte reserved_3;		/* Reserved */
+	unsigned long first_block;	/* First Block Location */
+	unsigned long last_block;	/* Last Block Location (Optional) */
+	byte reserved_12;		/* Reserved */
+	byte blocks_in_buffer_2;	/* Blocks In Buffer - MSB (Optional) */
+	byte blocks_in_buffer_1;
+	byte blocks_in_buffer_0;	/* Blocks In Buffer - LSB (Optional) */
+	unsigned long bytes_in_buffer;	/* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ *	REQUEST SENSE packet command result - Data Format.
+ */
+
+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 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 */
+	byte asl;			/* Additional sense length (n-7) */
+	unsigned long command_specific; /* Additional command specific information */
+	byte asc;			/* Additional Sense Code */
+	byte ascq;			/* Additional Sense Code Qualifier */
+	byte replaceable_unit_code;	/* Field Replaceable Unit Code */
+	unsigned sk_specific1 	:7;	/* Sense Key Specific */
+	unsigned sksv		:1;	/* Sense Key Specific informatio is valid */
+	byte sk_specific2;		/* Sense Key Specific */
+	byte sk_specific3;		/* Sense Key Specific */
+} idetape_request_sense_result_t;
+
+/*
+ *	Follows structures which are realted to the SELECT SENSE / MODE SENSE
+ *	packet commands. Those packet commands are still not supported
+ *	by ide-tape.
+ */
+
+#define	IDETAPE_CAPABILITIES_PAGE	0x2a
+
+/*
+ *	Mode Parameter Header for the MODE SENSE packet command
+ */
+
+typedef struct {
+	byte mode_data_length;		/* The length of the following data that is */
+					/* available to be transferred */
+	byte medium_type;		/* Medium Type */
+	byte dsp;			/* Device Specific Parameter */
+	byte bdl;			/* Block Descriptor Length */
+} idetape_mode_parameter_header_t;
+
+/*
+ *	Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ *	Support for block descriptors is optional.
+ */
+
+typedef struct {
+	byte density_code;		/* Medium density code */
+	byte blocks1;			/* Number of blocks - MSB */
+	byte blocks2;			/* Number of blocks - Middle byte */
+	byte blocks3;			/* Number of blocks - LSB */
+	byte reserved4;			/* Reserved */
+	byte length1;			/* Block Length - MSB */
+	byte length2;			/* Block Length - Middle byte */
+	byte length3;			/* Block Length - LSB */
+} idetape_parameter_block_descriptor_t;
+
+/*
+ *	The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+ 
+typedef struct {
+	unsigned page_code	:6;	/* Page Code - Should be 0xf */
+	unsigned reserved	:1;	/* Reserved */
+	unsigned ps		:1;
+	byte page_length;		/* Page Length - Should be 14 */
+	unsigned reserved2	:6;	/* Reserved */
+	unsigned dcc		:1;	/* Data Compression Capable */
+	unsigned dce		:1;	/* Data Compression Enable */
+	unsigned reserved3	:5;	/* Reserved */
+	unsigned red		:2;	/* Report Exception on Decompression */
+	unsigned dde		:1;	/* Data Decompression Enable */
+	unsigned long ca;		/* Compression Algorithm */
+	unsigned long da;		/* Decompression Algorithm */
+	byte reserved_12;		/* Reserved */
+	byte reserved_13;		/* Reserved */
+	byte reserved_14;		/* Reserved */
+	byte reserved_15;		/* Reserved */
+} idetape_data_compression_page_t;
+
+/*
+ *	The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+
+typedef struct {
+	unsigned page_code	:6;	/* Page Code - Should be 0x11 */
+	unsigned reserved1_6	:1;	/* Reserved */
+	unsigned ps		:1;
+	byte page_length;		/* Page Length - Should be 6 */
+	byte map;			/* Maximum Additional Partitions - Should be 0 */
+	byte apd;			/* Additional Partitions Defined - Should be 0 */
+	unsigned reserved4_012	:3;	/* Reserved */
+	unsigned psum		:2;	/* Should be 0 */
+	unsigned idp		:1;	/* Should be 0 */
+	unsigned sdp		:1;	/* Should be 0 */
+	unsigned fdp		:1;	/* Fixed Data Partitions */
+	byte mfr;			/* Medium Format Recognition */
+	byte reserved6;			/* Reserved */
+	byte reserved7;			/* Reserved */
+} idetape_medium_partition_page_t;
+
+/*
+ *	Prototypes of various functions in ide-tape.c
+ *
+ *	The following functions are called from ide.c, and their prototypes
+ *	are available in ide.h:
+ *
+ *		idetape_identify_device
+ *		idetape_setup
+ *		idetape_blkdev_ioctl
+ *		idetape_do_request
+ *		idetape_blkdev_open
+ *		idetape_blkdev_release
+ *		idetape_register_chrdev (void);
+ */
+
+/*
+ *	The following functions are used to transfer data from / to the
+ *	tape's data register.
+ */
+ 
+void idetape_input_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
+void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
+void idetape_discard_data (ide_drive_t *drive, unsigned long bcount);
+
+
+/*
+ *	Packet command related functions.
+ */
+ 
+void idetape_issue_packet_command  (ide_drive_t *drive,idetape_packet_command_t *pc,ide_handler_t *handler);
+void idetape_pc_intr (ide_drive_t *drive);
+
+/*
+ *	DSC handling functions.
+ */
+ 
+void idetape_postpone_request (ide_drive_t *drive);
+void idetape_poll_for_dsc (unsigned long data);
+void idetape_put_back_postponed_request (ide_drive_t *drive);
+void idetape_media_access_finished (ide_drive_t *drive);
+
+/*
+ *	Some more packet command related functions.
+ */
+ 
+void idetape_pc_callback (ide_drive_t *drive);
+void idetape_retry_pc (ide_drive_t *drive);
+void idetape_zero_packet_command (idetape_packet_command_t *pc);
+void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq);
+
+idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive);
+struct request *idetape_next_rq_storage (ide_drive_t *drive);
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+
+/*
+ *	Various packet commands
+ */
+ 
+void idetape_create_inquiry_cmd (idetape_packet_command_t *pc);
+void idetape_inquiry_callback (ide_drive_t *drive);
+void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition);
+void idetape_create_rewind_cmd (idetape_packet_command_t *pc);
+void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark);
+void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd);
+void idetape_create_erase_cmd (idetape_packet_command_t *pc);
+void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc);
+void idetape_create_read_position_cmd (idetape_packet_command_t *pc);
+void idetape_read_position_callback (ide_drive_t *drive);
+void idetape_create_read_cmd (idetape_packet_command_t *pc,unsigned long length);
+void idetape_read_callback (ide_drive_t *drive);
+void idetape_create_write_cmd (idetape_packet_command_t *pc,unsigned long length);
+void idetape_write_callback (ide_drive_t *drive);
+void idetape_create_request_sense_cmd (idetape_packet_command_t *pc);
+void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code);
+void idetape_request_sense_callback (ide_drive_t *drive);
+
+void idetape_display_inquiry_result (byte *buffer);
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result);
+
+/*
+ *	Character device callback functions.
+ *
+ *	We currently support:
+ *
+ *		OPEN, RELEASE, READ, WRITE and IOCTL.
+ */
+
+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);
+
+/*
+ *	idetape_mtioctop implements general magnetic tape io control
+ *	commands, as defined in include/linux/mtio.h. Those commands are
+ *	accessed through the character device interface, using the MTIOCTOP
+ *	ioctl.
+ */
+ 
+int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count);
+int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer);
+int idetape_queue_pc_tail (ide_drive_t *drive,idetape_packet_command_t *pc);
+
+void idetape_fake_read (ide_drive_t *drive);
+int idetape_position_tape (ide_drive_t *drive,unsigned long block);
+int idetape_rewind_tape (ide_drive_t *drive);
+
+/*
+ *	Used to get device information
+ */
+
+void idetape_get_mode_sense_results (ide_drive_t *drive);
+
+/*
+ *	General utility functions
+ */
+ 
+void idetape_fixstring (byte *s, const int bytecount, const int byteswap);
+unsigned long idetape_swap_long (unsigned long temp);
+unsigned short idetape_swap_short (unsigned short temp);
+
+/*
+ *	For general magnetic tape device compatibility.
+ */
+ 
+#include <linux/mtio.h>
+
+/*
+ *	Global variables
+ *
+ *	The variables below are used for the character device interface.
+ *
+ *	Additional state variables are defined in our ide_drive_t structure.
+ */
+ 
+idetape_chrdev_t idetape_chrdev;		/* Character device interface information */
+byte idetape_drive_already_found=0;		/* 1 when the above data structure is initialized */
+
+/*
+ *	Our character device supporting functions, passed to register_chrdev.
+ */
+ 
+static struct file_operations idetape_fops = {
+	NULL,			/* lseek - default */
+	idetape_chrdev_read,	/* read  */
+	idetape_chrdev_write,	/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	idetape_chrdev_ioctl,	/* ioctl */
+	NULL,			/* mmap */
+	idetape_chrdev_open,	/* open */
+	idetape_chrdev_release,	/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+
+/*
+ *	idetape_identify_device is called by do_identify in ide.c during
+ *	the device probing stage to check the contents of the ATAPI IDENTIFY
+ *	command results, in case the device type is tape. We return:
+ *
+ *	1	If the tape can be supported by us, based on the information
+ *		we have so far.
+ *
+ *	0 	If this tape driver is not currently supported by us.
+ *
+ *	In case we decide to support the tape, we store the current drive
+ *	pointer in our character device global variables, so that we can
+ *	pass between both interfaces.
+ */
+ 
+int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+
+{
+	struct idetape_id_gcw gcw;
+	unsigned short *ptr;
+	int support=1;
+#if IDETAPE_DEBUG
+	unsigned short mask,i;
+#endif /* IDETAPE_DEBUG */
+		
+	ptr=(unsigned short *) &gcw;
+	*ptr=id->config;
+
+#if IDETAPE_DEBUG
+	printk ("Dumping ATAPI Identify Device tape parameters\n");
+	
+	printk ("Protocol Type: ");
+	switch (gcw.protocol) {
+		case 0: case 1: printk ("ATA\n");break;
+		case 2:	printk ("ATAPI\n");break;
+		case 3: printk ("Reserved (Unknown to ide-tape)\n");break;
+	}
+	
+	printk ("Device Type: %x - ",gcw.device_type);	
+	switch (gcw.device_type) {
+		case 0: printk ("Direct-access Device\n");break;
+		case 1: printk ("Streaming Tape Device\n");break;
+		case 2: case 3: case 4: printk ("Reserved\n");break;
+		case 5: printk ("CD-ROM Device\n");break;
+		case 6: printk ("Reserved\n");
+		case 7: printk ("Optical memory Device\n");break;
+		case 0x1f: printk ("Unknown or no Device type\n");break;
+		default: printk ("Reserved\n");
+	}
+	printk ("Removable: %s",gcw.removable ? "Yes\n":"No\n");	
+		
+	printk ("Command Packet DRQ Type: ");
+	switch (gcw.drq_type) {
+		case 0: printk ("Microprocessor DRQ\n");break;
+		case 1: printk ("Interrupt DRQ\n");break;
+		case 2: printk ("Accelerated DRQ\n");break;
+		case 3: printk ("Reserved\n");break;
+	}
+	
+	printk ("Command Packet Size: ");
+	switch (gcw.packet_size) {
+		case 0: printk ("12 bytes\n");break;
+		case 1: printk ("16 bytes\n");break;
+		default: printk ("Reserved\n");break;
+	}
+	printk ("Model: %s\n",id->model);
+	printk ("Firmware Revision: %s\n",id->fw_rev);
+	printk ("Serial Number: %s\n",id->serial_no);
+	printk ("Write buffer size: %d bytes\n",id->buf_size*512);
+	printk ("DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+	printk ("LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+	printk ("IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+	printk ("IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+	printk ("PIO Cycle Timing Category: %d\n",id->tPIO);
+	printk ("DMA Cycle Timing Category: %d\n",id->tDMA);
+	printk ("Single Word DMA supported modes: ");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_1word & mask)
+			printk ("%d ",i);
+		if (id->dma_1word & (mask << 8))
+			printk ("(active) ");
+	}
+	printk ("\n");
+
+	printk ("Multi Word DMA supported modes: ");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_mword & mask)
+			printk ("%d ",i);
+		if (id->dma_mword & (mask << 8))
+			printk ("(active) ");
+	}
+	printk ("\n");
+
+	if (id->field_valid & 0x0002) {
+		printk ("Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+		printk ("Minimum Multi-word DMA cycle per word: ");
+		if (id->eide_dma_min == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_dma_min);
+
+		printk ("Manafactuer\'s Recommended Multi-word cycle: ");
+		if (id->eide_dma_time == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_dma_time);
+
+		printk ("Minimum PIO cycle without IORDY: ");
+		if (id->eide_pio == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_pio);
+
+		printk ("Minimum PIO cycle with IORDY: ");
+		if (id->eide_pio_iordy == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_pio_iordy);
+		
+	}
+
+	else {
+		printk ("According to the device, fields 64-70 are not valid.\n");
+	}
+#endif /* IDETAPE_DEBUG */
+
+	/* Check that we can support this device */
+
+	if (gcw.protocol !=2 ) {
+		printk ("ide-tape: Protocol is not ATAPI\n");support=0;
+	}
+
+	if (gcw.device_type != 1) {
+		printk ("ide-tape: Device type is not set to tape\n");support=0;
+	}
+
+	if (!gcw.removable) {
+		printk ("ide-tape: The removable flag is not set\n");support=0;
+	}
+
+	if (gcw.drq_type != 2) {
+		printk ("ide-tape: Sorry, DRQ types other than Accelerated DRQ\n");
+		printk ("ide-tape: are still not supproted by the driver\n");support=0;
+	}
+
+	if (gcw.packet_size != 0) {
+		printk ("ide-tape: Packet size is not 12 bytes long\n");
+		if (gcw.packet_size == 1)
+			printk ("ide-tape: Sorry, padding to 16 bytes is still not supported\n");
+		support=0;			
+	}
+
+	if (idetape_drive_already_found) {
+		printk ("ide-tape: Sorry, only one ide tape drive is supported by the driver\n");
+		support=0;
+	}
+	else {
+		idetape_drive_already_found=1;
+		idetape_chrdev.drive=drive;
+		idetape_chrdev.major=IDETAPE_MAJOR;
+		idetape_chrdev.minor=0;
+		idetape_chrdev.name[0]='h';
+		idetape_chrdev.name[1]='t';
+		idetape_chrdev.name[2]='0';
+		idetape_chrdev.name[3]=0;
+	}
+
+	return (support);		/* In case support=0, we will not install the driver */
+}
+
+/*
+ *	idetape_register_chrdev calls register_chrdev to register our character
+ *	device interface. The connection to the ide_drive_t structure, which
+ *	is used by the entire ide driver is provided by our global variable
+ *	idetape_chrdev.drive, which was initialized earlier, during the device
+ *	probing stage.
+ */
+ 
+void idetape_register_chrdev (void)
+
+{
+	int major,minor;
+	ide_drive_t *drive;
+
+	if (!idetape_drive_already_found)
+		return;
+
+	drive=idetape_chrdev.drive;
+	major=idetape_chrdev.major;
+	minor=idetape_chrdev.minor;
+	
+	if (register_chrdev (major,idetape_chrdev.name,&idetape_fops)) {
+		printk ("Unable to register character device interface !\n");
+		/* ??? */
+	}
+	else {
+		printk ("ide-tape: %s <-> %s : Character device interface on major = %d\n",
+			drive->name,idetape_chrdev.name,major);
+	}
+}
+
+/*
+ *	idetape_setup is called from the ide driver in the partition table
+ *	identification stage, to:
+ *
+ *		1.	Initialize our various state variables.
+ *		2.	Ask the tape for its capabilities.
+ *		3.	Allocate a buffer which will be used for data
+ *			transfer. The buffer size is chosen based on
+ *			the recommendation which we received in step (2).
+ *
+ *	Note that at this point ide.c already assigned us an irq, so that
+ *	we can queue requests here and wait for their completion.
+ */
+ 
+void idetape_setup (ide_drive_t *drive)
+
+{
+	int buffer_size;
+	idetape_tape_t *tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_setup\n");
+#endif /* IDETAPE_DEBUG */	
+	
+	drive->ready_stat = 0;			/* With an ATAPI device, we can issue packet commands */
+						/* regardless of the state of DRDY */
+	tape->block_address=0;			
+	tape->block_address_valid=0;
+	tape->locate_to=0;
+	tape->locate_retries=0;
+	tape->pc_stack_index=0;
+	tape->failed_pc=NULL;
+	tape->postponed_rq=NULL;
+	tape->last_written_valid=0;
+	tape->busy=0;
+	
+	idetape_get_mode_sense_results (drive);
+
+	buffer_size=tape->capabilities.ctl*tape->tape_block_size;
+	tape->data_buffer=kmalloc (buffer_size,GFP_KERNEL);
+	if (tape->data_buffer == NULL) {
+		printk ("ide-tape: FATAL - Can not allocate %d bytes for data transfer buffer\n",buffer_size);
+		printk ("ide-tape: Aborting character device installation\n");
+		idetape_drive_already_found=0;
+		unregister_chrdev (idetape_chrdev.major,idetape_chrdev.name);
+		return;
+	}
+	printk ("ide-tape: Speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,buffer_size);
+	return;
+}
+
+/*
+ *	idetape_get_mode_sense_results asks the tape about its various
+ *	parameters. In particular, we will adjust our data transfer buffer
+ *	size to the recommended value as returned by the tape.
+ */
+
+void idetape_get_mode_sense_results (ide_drive_t *drive)
+
+{
+	int retval;
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_mode_parameter_header_t *header;
+	idetape_capabilities_page_t *capabilities;
+	idetape_packet_command_t pc;
+	
+	idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE);
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+	retval=idetape_queue_pc_tail (drive,&pc);
+
+	header=(idetape_mode_parameter_header_t *) pc.buffer;	
+	capabilities=(idetape_capabilities_page_t *) (pc.buffer+sizeof (idetape_mode_parameter_header_t));
+
+	capabilities->max_speed=idetape_swap_short (capabilities->max_speed);
+	capabilities->ctl=idetape_swap_short (capabilities->ctl);
+	capabilities->speed=idetape_swap_short (capabilities->speed);
+	capabilities->buffer_size=idetape_swap_short (capabilities->buffer_size);
+
+	tape->capabilities=*capabilities;		/* Save us a copy */
+	tape->tape_block_size=capabilities->blk512 ? 512:1024;
+
+	if (retval) {
+		printk ("ide-tape: Can't get tape parameters\n");
+		printk ("ide-tape: Assuming some default parameters\n");
+		tape->tape_block_size=512;
+		tape->capabilities.ctl=26*1024;
+		return;
+	}
+
+#if IDETAPE_DEBUG
+	printk ("Dumping the results of the MODE SENSE packet command\n");
+	printk ("Mode Parameter Header:\n");
+	printk ("Mode Data Length - %d\n",header->mode_data_length);
+	printk ("Medium Type - %d\n",header->medium_type);
+	printk ("Device Specific Parameter - %d\n",header->dsp);
+	printk ("Block Descriptor Length - %d\n",header->bdl);
+	
+	printk ("Capabilities and Mechanical Status Page:\n");
+	printk ("Page code - %d\n",capabilities->page_code);
+	printk ("Page length - %d\n",capabilities->page_length);
+	printk ("Read only - %s\n",capabilities->ro ? "Yes":"No");
+	printk ("Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No");
+	printk ("Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No");
+	printk ("Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No");
+	printk ("Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No");
+	printk ("The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No");
+	printk ("The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No");
+	printk ("Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No");
+	printk ("Supports error correction - %s\n",capabilities->ecc ? "Yes":"No");
+	printk ("Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No");
+	printk ("Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No");
+	printk ("Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No");
+	printk ("Restricted byte count for PIO transfers - %s\n",capabilities->slowb ? "Yes":"No");
+	printk ("Maximum supported speed in KBps - %d\n",capabilities->max_speed);
+	printk ("Continuous transfer limits in blocks - %d\n",capabilities->ctl);
+	printk ("Current speed in KBps - %d\n",capabilities->speed);	
+	printk ("Buffer size - %d\n",capabilities->buffer_size*512);
+#endif /* IDETAPE_DEBUG */
+}
+
+/*
+ *	Packet Command Interface
+ *
+ *	The current Packet Command is available in tape->pc, and will not
+ *	change until we finish handling it. Each packet command is associated
+ *	with a callback function that will be called when the command is
+ *	finished.
+ *
+ *	The handling will be done in three stages:
+ *
+ *	1.	idetape_issue_packet_command will send the packet command to the
+ *		drive, and will set the interrupt handler to idetape_pc_intr.
+ *
+ *	2.	On each interrupt, idetape_pc_intr will be called. This step
+ *		will be repeated until the device signals us that no more
+ *		interrupts will be issued.
+ *
+ *	3.	ATAPI Tape media access commands have immediate status with a
+ *		delayed process. In case of a successfull initiation of a
+ *		media access packet command, the DSC bit will be set when the
+ *		actual execution of the command is finished. 
+ *		Since the tape drive will not issue an interrupt, we have to
+ *		poll for this event. In this case, we define the request as
+ *		"low priority request" by setting rq_status to
+ *		IDETAPE_RQ_POSTPONED, 	set a timer to poll for DSC and exit
+ *		the driver.
+ *
+ *		ide.c will then give higher priority to requests which
+ *		originate from the other device, until will change rq_status
+ *		to RQ_ACTIVE.
+ *
+ *	4.	When the packet command is finished, it will be checked for errors.
+ *
+ *	5.	In case an error was found, we queue a request sense packet command
+ *		in front of the request queue and retry the operation up to
+ *		IDETAPE_MAX_PC_RETRIES times.
+ *
+ *	6.	In case no error was found, or we decided to give up and not
+ *		to retry again, the callback function will be called and then
+ *		we will handle the next request.
+ *
+ */
+
+void idetape_issue_packet_command  (ide_drive_t *drive,idetape_packet_command_t *pc,ide_handler_t *handler)
+
+{
+	idetape_tape_t *tape;
+	idetape_bcount_reg_t bcount;
+        idetape_ireason_reg_t ireason;
+
+	tape=&(drive->tape);
+	        
+#ifdef IDETAPE_DEBUG
+	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");
+		/* ??? Need to rethink about that */		
+	}
+#endif /* IDETAPE_DEBUG */
+
+	if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
+		tape->failed_pc=pc;
+	tape->pc=pc;							/* Set the current packet command */
+
+	if (pc->retries > IDETAPE_MAX_PC_RETRIES) {
+		printk ("ide-tape: %s: I/O error, ",drive->name);
+		printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq);
+		printk ("ide-tape: Maximum retries reached - Giving up\n");
+		pc->error=1;					/* Giving up */
+		pc->active=0;
+		tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif /* IDETAPE_DEBUG */
+			(*pc->callback)(drive);
+		return;
+	}
+
+#if IDETAPE_DEBUG
+	printk ("Retry number - %d\n",pc->retries);
+#endif /* IDETAPE_DEBUG */
+
+	pc->retries++;
+
+/*
+ *	We no longer call ide_wait_stat to wait for the drive to be ready,
+ *	as ide.c already does this for us in do_request.
+ */
+ 
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->active=1;							/* Packet command started */
+	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 */
+	OUT_BYTE (bcount.b.high,IDETAPE_BCOUNTH_REG);
+	OUT_BYTE (bcount.b.low,IDETAPE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDETAPE_DRIVESEL_REG);
+	
+	ide_set_handler (drive,handler,WAIT_CMD);			/* Set the interrupt routine */
+	OUT_BYTE (WIN_PACKETCMD,IDETAPE_ATACOMMAND_REG);		/* Issue the packet command */
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { 	/* Wait for DRQ to be ready - Assuming Accelerated DRQ */	
+		/*
+		 *	We currently only support tape drives which report
+		 *	accelerated DRQ assertion. For this case, specs
+		 *	allow up to 50us. We really shouldn't get here.
+		 *
+		 *	??? Still needs to think what to do if we reach
+		 *	here anyway.
+		 */
+		 
+		 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_output_data (drive,pc->c,12/4);			/* Send the actual packet */
+}
+
+/*
+ *	idetape_pc_intr is the usual interrupt handler which will be called
+ *	during a packet command. We will transfer some of the data (as
+ *	requested by the drive) and will re-point interrupt handler to us.
+ *	When data transfer is finished, we will act according to the
+ *	algorithm described before idetape_issue_packet_command.
+ *
+ */
+ 
+
+void idetape_pc_intr (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	idetape_status_reg_t status;
+	idetape_bcount_reg_t bcount;
+        idetape_ireason_reg_t ireason;
+	idetape_packet_command_t *pc;
+	
+	unsigned long temp;
+
+	tape=&(drive->tape);
+	
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);		/* Clear the interrupt */
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_pc_intr interrupt handler\n");
+#endif /* IDETAPE_DEBUG */	
+
+	pc=tape->pc;						/* Current packet command */
+		
+	if (!status.b.drq) {					/* No more interrupts */
+#if IDETAPE_DEBUG
+		printk ("Packet command completed\n");
+		printk ("Total bytes transferred: %lu\n",pc->actually_transferred);
+#endif /* IDETAPE_DEBUG */
+		if (status.b.check) {					/* Error detected */
+#if IDETAPE_DEBUG
+	/*
+	 *	Without debugging, we only log an error if we decided to
+	 *	give up retrying.
+	 */
+			printk ("ide-tape: %s: I/O error, ",drive->name);
+#endif /* IDETAPE_DEBUG */
+			idetape_retry_pc (drive);			/* Retry operation */
+			return;
+		}
+		pc->error=0;
+		if (pc->wait_for_dsc && !status.b.dsc) {				/* Media access command */
+			tape->dsc_polling_frequency=IDETAPE_DSC_FAST_MEDIA_ACCESS_FREQUENCY;
+			idetape_postpone_request (drive);		/* Allow ide.c to handle other requests */
+			return;
+		}
+		pc->active=0;
+		if (tape->failed_pc == pc)
+			tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)			
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif IDETAPE_DEBUG
+			(*pc->callback)(drive);			/* Command finished - Call the callback function */
+		return;
+	}
+
+	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");
+		/* ??? */
+	}
+	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");
+		/* ??? */		
+	}
+	
+	if (!pc->writing) {					/* Reading - Check that we have enough space */
+		temp=(unsigned long) pc->actually_transferred + bcount.all;
+		if ( temp > pc->request_transfer) {
+			printk ("ide-tape: The tape wants to send us more data than requested - ");
+			if (temp > pc->buffer_size) {
+				printk ("Discarding data\n");
+				idetape_discard_data (drive,bcount.all);
+				ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);
+				return;
+			}
+			else
+				printk ("Allowing transfer\n");
+		}
+	}
+#if IDETAPE_DEBUG	
+	if (bcount.all && !pc->buffer) {	
+		printk ("ide-tape: ide-tape.c bug - Buffer not set in idetape_pc_intr. Discarding data.\n");
+		
+		if (!pc->writing) {
+			printk ("ide-tape: Discarding data\n");
+			idetape_discard_data (drive,bcount.all);
+			ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);
+			return;
+		}
+		else {	/* ??? */
+		}
+	}
+#endif /* IDETAPE_DEBUG */
+	if (pc->writing)
+		idetape_output_data (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+	else
+		idetape_input_data (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+#if IDETAPE_DEBUG
+	printk ("ide-tape: %s %d bytes\n",pc->writing ? "Wrote":"Received",bcount.all);
+#endif /* IDETAPE_DEBUG */
+	pc->actually_transferred+=bcount.all;					/* Update the current position */
+	pc->current_position+=bcount.all;
+
+	ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);		/* And set the interrupt handler again */
+}
+
+/*
+ *	idetape_postpone_request postpones the current request so that
+ *	ide.c will be able to service requests from another device on
+ *	the same hwgroup while we are polling for DSC.
+ */
+
+void idetape_postpone_request (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	unsigned long flags;
+	struct request *rq;
+		
+	tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_postpone_request\n");
+	if (tape->postponed_rq != NULL)
+		printk ("ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
+#endif /* IDETAPE_DEBUG */
+
+	tape->dsc_count=0;
+	tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency;	/* Set timer to poll for */
+	tape->dsc_timeout=jiffies+IDETAPE_DSC_TIMEOUT;			/* actual completion */
+	tape->dsc_timer.data=(unsigned long) drive;
+	tape->dsc_timer.function=&idetape_poll_for_dsc;
+	init_timer (&(tape->dsc_timer));
+
+	/*
+	 * Remove current request from the request queue:
+	 */
+	save_flags(flags);						/* Let ide.c handle another request */
+	cli();
+	tape->postponed_rq = rq = HWGROUP(drive)->rq;
+	rq->rq_status = IDETAPE_RQ_POSTPONED;	
+	blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+	HWGROUP(drive)->rq = NULL;
+	restore_flags(flags);
+
+	tape->dsc_polling_start=jiffies;	
+	add_timer(&(tape->dsc_timer));		/* Activate the polling timer */
+}
+
+
+/*
+ *	idetape_poll_for_dsc gets invoked by a timer (which was set
+ *	by idetape_postpone_request) to poll for the DSC bit
+ *	in the status register. When the DSC bit is set, or a timeout is
+ *	reached, we put back the postponed request in front of the request
+ *	queue.
+ */
+ 
+void idetape_poll_for_dsc (unsigned long data)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	
+	idetape_status_reg_t status;
+	idetape_packet_command_t *pc;
+
+	drive=(ide_drive_t *) data;
+	tape=&(drive->tape);
+	pc=tape->pc;
+	
+#if IDETAPE_DEBUG
+	printk ("%s: idetape_poll_for_dsc called\n",drive->name);
+#endif /* IDETAPE_DEBUG */	
+
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);
+
+	if (status.b.dsc)
+		tape->dsc_count++;
+	else {
+		if (tape->dsc_count)
+			printk ("ide-tape: DSC fluctuation detected - Restarting DSC count\n");
+		tape->dsc_count=0;
+	}
+
+	if (tape->dsc_count == IDETAPE_DSC_COUNT) {		/* DSC received */
+		tape->dsc_received=1;
+		del_timer (&(tape->dsc_timer));			/* Stop polling and put back the postponed */
+		idetape_put_back_postponed_request (drive);	/* request in the request queue */
+		return;
+	}
+		
+	if (jiffies > tape->dsc_timeout) 	{ /* Timeout */
+		tape->dsc_received=0;
+		del_timer (&(tape->dsc_timer));
+		/* ??? */
+		idetape_put_back_postponed_request (drive);
+		return;
+	}
+	
+	/* Poll again */
+
+	if (jiffies - tape->dsc_polling_start > IDETAPE_FAST_SLOW_THRESHOLD)
+		tape->dsc_timer.expires = jiffies + IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY;
+	else
+		tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency;
+/*	init_timer (&(tape->dsc_timer)); */
+	add_timer(&(tape->dsc_timer));
+	return;
+}
+
+/*
+ *	idetape_put_back_postponed_request gets called by
+ *	idetape_poll_for_dsc when we decided to stop polling - Either
+ *	becase we received DSC or because we decided to give up and
+ *	stop waiting.
+ */
+
+void idetape_put_back_postponed_request (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Putting back postponed request\n");
+#endif /* IDETAPE_DEBUG */
+
+	(void) ide_do_drive_cmd (drive, tape->postponed_rq, ide_next);
+
+	/*
+	 * 	Note that the procedure done here is differnet than the method
+	 *	we are using in idetape_queue_pc_head - There we are putting
+	 *	request(s) before our currently called request.
+	 *
+	 *	Here, on the other hand, HWGROUP(drive)->rq is not our
+	 *	request but rather a request to another device. Therefore,
+	 *	we will let it finish and only then service our postponed
+	 *	request --> We don't touch HWGROUP(drive)->rq.
+	 */
+}
+
+void idetape_media_access_finished (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_status_reg_t status;
+	idetape_packet_command_t *pc;
+
+	pc=tape->pc;
+	
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);
+
+	if (tape->dsc_received) {
+#if IDETAPE_DEBUG
+		printk ("DSC received\n");
+#endif /* IDETAPE_DEBUG */
+		pc->active=0;
+		if (status.b.check) {					/* Error detected */
+			printk ("ide-tape: %s: I/O error, ",drive->name);
+			idetape_retry_pc (drive);			/* Retry operation */
+			return;
+		}
+		pc->error=0;
+		if (tape->failed_pc == pc)
+			tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif /* IDETAPE_DEBUG */
+			(*pc->callback)(drive);
+
+		return;
+	}
+	else {
+		pc->active=0;
+		printk ("ide-tape: %s: DSC timeout.\n",drive->name);
+		/* ??? */
+		pc->error=1;
+		tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif /* IDETAPE_DEBUG */
+			(*pc->callback)(drive);
+		return;
+	}
+}
+
+
+/*
+ *	idetape_retry_pc is called when an error was detected during the
+ *	last packet command. We queue a request sense packet command in
+ *	the head of the request list.
+ */
+ 
+void idetape_retry_pc (ide_drive_t *drive)
+
+{
+	idetape_packet_command_t *pc;
+	struct request *new_rq;
+
+	idetape_error_reg_t error;
+	error.all=IN_BYTE (IDETAPE_ERROR_REG);
+	pc=idetape_next_pc_storage (drive);
+	new_rq=idetape_next_rq_storage (drive);
+	idetape_create_request_sense_cmd (pc); 
+	pc->buffer=pc->temp_buffer;
+	pc->buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc->current_position=pc->temp_buffer;
+	idetape_queue_pc_head (drive,pc,new_rq);
+}
+
+/*
+ *	General packet command callback function.
+ */
+ 
+void idetape_pc_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	
+	tape=&(drive->tape);
+	rq=HWGROUP(drive)->rq;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_pc_callback\n");
+#endif /* IDETAPE_DEBUG */
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+
+void idetape_read_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+
+	tape=&(drive->tape);	
+	rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_read_callback\n");
+#endif /* IDETAPE_DEBUG */
+	tape->block_address+=tape->pc->actually_transferred/512;
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		rq->sector+=rq->current_nr_sectors;
+		rq->nr_sectors-=rq->current_nr_sectors;
+		rq->current_nr_sectors=0;
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+void idetape_fake_read (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	unsigned long i;
+
+	tape=&(drive->tape);	
+	rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_fake_read\n");
+#endif /* IDETAPE_DEBUG */
+
+#if IDETAPE_DEBUG
+	printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+	
+	for (i=0;i<rq->current_nr_sectors*512;i++)
+		rq->buffer [i]=0;
+
+	tape->block_address+=rq->current_nr_sectors;
+	rq->sector+=rq->current_nr_sectors;
+	rq->nr_sectors-=rq->current_nr_sectors;
+	rq->current_nr_sectors=0;
+	idetape_end_request (1,HWGROUP (drive));
+}
+
+void idetape_write_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	
+	tape=&(drive->tape);
+	rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_write_callback\n");
+#endif /* IDETAPE_DEBUG */
+	tape->block_address+=tape->pc->actually_transferred/512;
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		rq->sector+=rq->current_nr_sectors;
+		rq->nr_sectors-=rq->current_nr_sectors;
+		rq->current_nr_sectors=0;
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+void idetape_inquiry_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+	
+	idetape_display_inquiry_result (tape->pc->buffer);
+	idetape_pc_callback (drive);
+	return;
+}
+
+/*
+ *	idetape_input_data is called to read data from the tape's data
+ *	register. We basically let ide_input_data do the job, but we also
+ *	take care about the remaining bytes which can not be transferred
+ *	in 32-bit data transfers.
+ */
+ 
+void idetape_input_data (ide_drive_t *drive,void *buffer, unsigned long bcount)
+
+{
+	unsigned long wcount;
+	
+	wcount=bcount >> 2;
+	bcount -= 4*wcount;
+	
+	if (wcount)
+		ide_input_data (drive,buffer,wcount);
+	
+	if (bcount) {
+		((byte *)buffer) += 4*wcount;
+		insb (IDETAPE_DATA_REG,buffer,bcount);
+	}
+}
+
+/*
+ *	idetape_output_data is used to write data to the tape.
+ */
+ 
+void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount)
+
+{
+	unsigned long wcount;
+	
+	wcount=bcount >> 2;
+	bcount -= 4*wcount;
+	
+	if (wcount)
+		ide_output_data (drive,buffer,wcount);
+	
+	if (bcount) {
+		((byte *)buffer) += 4*wcount;
+		outsb (IDETAPE_DATA_REG,buffer,bcount);
+	}
+}
+
+/*
+ *	Too bad. The drive wants to send us data which we are not ready to accept.
+ *	Just throw it away.
+ */
+ 
+void idetape_discard_data (ide_drive_t *drive, unsigned long bcount)
+
+{
+	unsigned long i;
+	
+	for (i=0;i<bcount;i++)
+		IN_BYTE (IDETAPE_DATA_REG);
+}
+
+/*
+ *	Issue an INQUIRY packet command.
+ */
+ 
+void idetape_create_inquiry_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating INQUIRY packet command\n");
+#endif /* IDETAPE_DEBUG */	
+	pc->request_transfer=36;
+	pc->callback=&idetape_inquiry_callback;
+	pc->writing=0;
+	
+	idetape_zero_packet_command (pc);		
+	pc->c[0]=IDETAPE_INQUIRY_CMD;
+	pc->c[4]=255;
+}
+
+/*
+ *	Format the INQUIRY command results.
+ */
+ 
+void idetape_display_inquiry_result (byte *buffer)
+
+{
+	idetape_inquiry_result_t *result;
+
+	result=(idetape_inquiry_result_t *) buffer;
+	idetape_fixstring (result->vendor_id,8,0);
+	idetape_fixstring (result->product_id,16,0);
+	idetape_fixstring (result->revision_level,4,0);
+
+	if (result->response_format != 2) {
+		printk ("The INQUIRY Data Format is unknown to us !\n");
+		printk ("Assuming QIC-157C format.\n");
+	}
+
+#if IDETAPE_DEBUG
+	printk ("Dumping INQUIRY command results:\n");
+	printk ("Response Data Format: %d - ",result->response_format);
+	switch (result->response_format) {
+		case 2:
+			printk ("As specified in QIC-157 Revision C\n");
+			break;
+		default:
+			printk ("Unknown\n");
+			break;
+	}
+	
+	printk ("Device Type: %x - ",result->device_type);	
+	switch (result->device_type) {
+		case 0: printk ("Direct-access Device\n");break;
+		case 1: printk ("Streaming Tape Device\n");break;
+		case 2: case 3: case 4: printk ("Reserved\n");break;
+		case 5: printk ("CD-ROM Device\n");break;
+		case 6: printk ("Reserved\n");
+		case 7: printk ("Optical memory Device\n");break;
+		case 0x1f: printk ("Unknown or no Device type\n");break;
+		default: printk ("Reserved\n");
+	}
+	
+	printk ("Removable Medium: %s",result->rmb ? "Yes\n":"No\n");
+
+	printk ("ANSI Version: %d - ",result->ansi_version);
+	switch (result->ansi_version) {
+		case 2:
+			printk ("QIC-157 Revision C\n");
+			break;
+		default:
+			printk ("Unknown\n");
+			break;
+	}
+
+	printk ("ECMA Version: ");
+	if (result->ecma_version)
+		printk ("%d\n",result->ecma_version);
+	else
+		printk ("Not supported\n");
+
+	printk ("ISO Version: ");
+	if (result->iso_version)
+		printk ("%d\n",result->iso_version);
+	else
+		printk ("Not supported\n");
+
+	printk ("Additional Length: %d\n",result->additional_length);
+	printk ("Vendor Identification: %s\n",result->vendor_id);
+	printk ("Product Identification: %s\n",result->product_id);
+	printk ("Product Revision Level: %s\n",result->revision_level);
+#endif /* IDETAPE_DEBUG */
+
+	if (result->device_type != 1)
+		printk ("Device type is not set to tape\n");
+
+	if (!result->rmb)
+		printk ("The removable flag is not set\n");
+
+	if (result->ansi_version != 2) {
+		printk ("The Ansi Version is unknown to us !\n");
+		printk ("Assuming compliance with QIC-157C specification.\n");
+	}
+}
+
+void idetape_create_request_sense_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating REQUEST SENSE packet command\n");
+#endif /* IDETAPE_DEBUG */	
+	pc->request_transfer=18;
+	pc->callback=&idetape_request_sense_callback;
+	pc->writing=0;
+	
+	idetape_zero_packet_command (pc);	
+	pc->c[0]=IDETAPE_REQUEST_SENSE_CMD;
+	pc->c[4]=255;
+}
+
+void idetape_request_sense_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+
+	tape=&(drive->tape);	
+	rq=HWGROUP(drive)->rq;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_request_sense_callback\n");
+#endif /* IDETAPE_DEBUG */
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Error in REQUEST SENSE itself - Aborting request!\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+/*
+ *	idetape_analyze_error is called on each failed packet command retry
+ *	to analyze the request sense. We currently do not utilize this
+ *	information.
+ */
+ 
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+	tape->sense_key=result->sense_key;
+	tape->asc=result->asc;
+	tape->ascq=result->ascq;
+#if IDETAPE_DEBUG	
+	/*
+	 *	Without debugging, we only log an error if we decided to
+	 *	give up retrying.
+	 */
+	printk ("ide-tape: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDETAPE_DEBUG */	
+	return;	
+}
+
+void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating TEST UNIT READY packet command\n");
+#endif /* IDETAPE_DEBUG */	
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+	
+	idetape_zero_packet_command (pc);	
+	pc->c[0]=IDETAPE_TEST_UNIT_READY_CMD;
+}
+
+void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition)
+
+{
+	unsigned long *ptr;
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating LOCATE packet command\n");
+#endif /* IDETAPE_DEBUG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_LOCATE_CMD;
+	pc->c [1]=2;
+	ptr=(unsigned long *) &(pc->c[3]);
+	*ptr=idetape_swap_long (block);
+	pc->c[8]=partition;
+}
+
+void idetape_create_rewind_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating REWIND packet command\n");
+#endif /* IDETAPE_DEBUG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_REWIND_CMD;
+}
+
+/*
+ *	A mode sense command is used to "sense" tape parameters.
+ */
+
+void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code)
+
+{
+#if IDETAPE_DEBUG
+		printk ("ide-tape: Creating MODE SENSE packet command - Page %d\n",page_code);
+#endif /* IDETAPE_DEBUG */
+
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+
+	switch (page_code) {
+		case IDETAPE_CAPABILITIES_PAGE:
+			pc->request_transfer=24;
+	}
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_MODE_SENSE_CMD;
+	pc->c [1]=8;				/* DBD = 1 - Don't return block descriptors for now */
+	pc->c [2]=page_code;
+	pc->c [3]=255;				/* Don't limit the returned information */
+	pc->c [4]=255;				/* (We will just discard data in that case) */
+}
+
+/*
+ *	idetape_create_write_filemark_cmd will:
+ *
+ *		1.	Write a filemark if write_filemark=1.
+ *		2.	Flush the device buffers without writing a filemark
+ *			if write_filemark=0.
+ *
+ */
+ 
+void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark)
+
+{
+#if IDETAPE_DEBUG
+	printk ("Creating WRITE FILEMARK packet command\n");
+	if (!write_filemark)
+		printk ("which will only flush buffered data\n");
+#endif /* IDETAPE_DEBUG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_WRITE_FILEMARK_CMD;
+	if (write_filemark)
+		pc->c [4]=1;
+}
+
+void idetape_create_erase_cmd (idetape_packet_command_t *pc)
+
+{
+
+#if IDETAPE_DEBUG
+	printk ("Creating ERASE command\n");
+#endif /* IDETAPE_DEBUG */
+
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_ERASE_CMD;
+	pc->c [1]=1;
+}
+
+void idetape_create_read_cmd (idetape_packet_command_t *pc,unsigned long length)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating READ packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	original.all=length;
+
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_read_callback;
+	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;
+
+	return;
+}
+
+void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating SPACE packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	original.all=count;
+
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_SPACE_CMD;
+	pc->c [1]=cmd;
+	pc->c [4]=original.b.b1;
+	pc->c [3]=original.b.b2;
+	pc->c [2]=original.b.b3;
+
+	return;
+}
+
+void idetape_create_write_cmd (idetape_packet_command_t *pc,unsigned long length)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating WRITE packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	original.all=length;
+
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_write_callback;
+	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;
+
+	return;
+}
+
+void idetape_create_read_position_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating READ POSITION packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	pc->request_transfer=20;
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_read_position_callback;
+	pc->writing=0;
+
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_READ_POSITION_CMD;
+	pc->c [1]=0;
+}
+
+void idetape_read_position_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	idetape_read_position_result_t *result;
+	
+	tape=&(drive->tape);
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_read_position_callback\n");
+#endif /* IDETAPE_DEBUG */
+
+	rq=HWGROUP(drive)->rq;
+	
+	if (!tape->pc->error) {
+		result=(idetape_read_position_result_t *) tape->pc->buffer;
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+		printk ("Dumping the results of the READ POSITION command\n");
+		printk ("BOP - %s\n",result->bop ? "Yes":"No");
+		printk ("EOP - %s\n",result->eop ? "Yes":"No");
+#endif /* IDETAPE_DEBUG */
+		if (result->bpu) {
+			printk ("ide-tape: Block location is unknown to the tape\n");
+			printk ("Aborting request\n");
+			tape->block_address_valid=0;
+			idetape_end_request (0,HWGROUP (drive));
+		}
+		else {
+#if IDETAPE_DEBUG
+			printk ("Block Location - %lu\n",idetape_swap_long (result->first_block));
+#endif /* IDETAPE_DEBUG */
+			tape->block_address=idetape_swap_long (result->first_block);
+			tape->block_address_valid=1;
+			idetape_end_request (1,HWGROUP (drive));
+		}
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+/*
+ *	Our special ide-tape ioctl's.
+ *
+ *	Currently there aren't any significant ioctl's.
+ *	mtio.h compatible commands should be issued to the character device
+ *	interface.
+ */
+ 
+int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			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;
+	pc.current_position=pc.temp_buffer;
+
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_blkdev_ioctl\n");
+#endif /* IDETAPE_DEBUG */
+	switch (cmd) {
+		case IDETAPE_INQUIRY_IOCTL:
+#if IDETAPE_DEBUG
+			printk ("Adding INQUIRY packet command to the tail of the request queue\n");
+#endif /* IDETAPE_DEBUG */
+			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
+			printk ("Adding LOCATE packet command to the tail of the request queue\n");
+#endif /* IDETAPE_DEBUG */
+			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;
+	}
+}
+
+/*
+ *	Functions which handle requests.
+ */
+
+/*
+ *	idetape_end_request is used to end a request.
+ *
+ *	It is very similiar to ide_end_request, with a major difference - If
+ *	we are handling our own requests rather than requests which originate
+ *	in the buffer cache, we set rq->errors to 1 if the request failed.
+ */
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+
+{
+	ide_drive_t *drive = hwgroup->drive;
+	struct request *rq = hwgroup->rq;
+
+	if (rq->cmd == READ || rq->cmd == WRITE) {	/* Buffer cache originated request */
+		ide_end_request (uptodate,hwgroup);	/* Let the common code handle it */
+		return;
+	}
+							/* Our own originated request */
+	rq->errors=!uptodate;				/* rq->errors will tell us if the request was successfull */
+	ide_end_drive_cmd (drive, 0, 0);
+
+	/* The "up(rq->sem);" does the necessary "wake_up()" for us,
+	 * providing we started sleeping with a "down()" call.
+	 * This may not be the case if the driver converts a READ or WRITE
+	 * request into a special internal rq->cmd type.   -ml
+	 */
+	 
+	 /*
+	  * As Mark explained, we do not need a "wake_up()" call here,
+	  * since we are always sleeping with a "down()" call.
+	  */
+
+}
+
+/*
+ *	idetape_do_request is our request handling function.	
+ */
+
+void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+
+{
+	idetape_tape_t *tape;
+	idetape_packet_command_t *pc;
+	struct request *new_rq;
+	idetape_status_reg_t status;
+
+	tape=&(drive->tape);
+		
+#if IDETAPE_DEBUG
+	printk ("Current request:\n");
+	printk ("rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk ("sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDETAPE_DEBUG */
+
+	/* Retry a failed packet command */
+
+	if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+		idetape_issue_packet_command (drive,tape->failed_pc,&idetape_pc_intr);
+		return;
+	}
+
+	/* Check if we have a postponed request */
+	
+	if (tape->postponed_rq != NULL) {
+/* #if IDETAPE_DEBUG */
+		if (tape->postponed_rq->rq_status != RQ_ACTIVE || rq != tape->postponed_rq) {
+			printk ("ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
+			idetape_end_request (0,HWGROUP (drive));
+			return;
+		}
+/* #endif */ /* IDETAPE_DEBUG */
+		if (rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
+	
+			/* Media access command */
+			
+			tape->postponed_rq = NULL;
+			idetape_media_access_finished (drive);
+			return;
+		}
+		
+		/*
+		 * Read / Write command - DSC polling was done before the
+		 * actual command - Continue normally so that the command
+		 * will be performed below.
+		 */
+		 
+		 tape->postponed_rq = NULL;
+	}	
+	
+
+	if (rq->cmd == READ || rq->cmd == IDETAPE_READ_REQUEST || rq->cmd == WRITE || rq->cmd == IDETAPE_WRITE_REQUEST) {
+
+		if (!tape->block_address_valid || tape->block_address!=rq->sector) {		/* Re-position the tape */
+		
+			if (tape->locate_to == rq->sector && tape->locate_retries > IDETAPE_LOCATE_RETRIES) {
+				printk ("ide-tape: Can not reach block %lu - Aborting request\n",rq->sector);
+				tape->locate_retries=0;
+				idetape_end_request (0,HWGROUP (drive));
+				return;
+			}
+						
+			if (tape->locate_to == rq->sector)
+				tape->locate_retries++;
+			else {
+				tape->locate_to=rq->sector;
+				tape->locate_retries=1;
+			}
+#if IDETAPE_DEBUG
+			printk ("ide-tape: We are not at the requested block\n");
+			printk ("ide-tape: Re-positioning tape\n");
+			printk ("ide-tape: Adding READ POSITION command to the head of the queue\n");
+#endif /* IDETAPE_DEBUG */
+			pc=idetape_next_pc_storage (drive);
+			new_rq=idetape_next_rq_storage (drive);
+			idetape_create_read_position_cmd (pc); 
+			pc->buffer=pc->temp_buffer;
+			pc->buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+			pc->current_position=pc->temp_buffer;
+			idetape_queue_pc_head (drive,pc,new_rq);
+#if IDETAPE_DEBUG			
+			printk ("ide-tape: Adding LOCATE %lu command to the head of the queue\n",rq->sector);
+#endif /* IDETAPE_DEBUG */
+			pc=idetape_next_pc_storage (drive);
+			new_rq=idetape_next_rq_storage (drive);
+			idetape_create_locate_cmd (pc,rq->sector,0);
+			idetape_queue_pc_head (drive,pc,new_rq);
+
+			if (!tape->block_address_valid) {		/* The tape doesn't know the position - help it */
+									/* by rewinding the tape */
+#if IDETAPE_DEBUG			
+				printk ("ide-tape: Adding LOCATE 0 command to the head of the queue\n");
+#endif /* IDETAPE_DEBUG */
+				pc=idetape_next_pc_storage (drive);
+				new_rq=idetape_next_rq_storage (drive);
+				idetape_create_locate_cmd (pc,0,0);
+				idetape_queue_pc_head (drive,pc,new_rq);
+			}
+			
+			return;
+		}
+		else
+			tape->locate_retries=0;
+	}
+	
+	switch (rq->cmd) {
+		case READ:
+		case IDETAPE_READ_REQUEST:
+#if IDETAPE_DEBUG
+			if (rq->cmd == READ)
+				printk ("ide-tape: Handling buffer cache READ request\n");
+			else
+				printk ("ide-tape: Handling our own (not buffer cache originated) READ request\n");
+#endif /* IDETAPE_DEBUG */			
+			status.all=IN_BYTE (IDETAPE_STATUS_REG);
+			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
+#if IDETAPE_DEBUG
+				printk ("ide-tape: DSC != 1 - Postponing read request\n");
+#endif /* IDETAPE_DEBUG */	
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
+				return;
+			}			
+
+			tape->last_written_valid=0;
+			
+			pc=idetape_next_pc_storage (drive);
+
+			idetape_create_read_cmd (pc,rq->current_nr_sectors);
+			
+			pc->buffer=rq->buffer;
+			pc->buffer_size=rq->current_nr_sectors*512;
+			pc->current_position=rq->buffer;
+			pc->request_transfer=rq->current_nr_sectors*512;
+
+			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+			return;
+		
+		case WRITE:
+		case IDETAPE_WRITE_REQUEST:
+#if IDETAPE_DEBUG
+			if (rq->cmd == WRITE)
+				printk ("ide-tape: Handling buffer cache WRITE request\n");
+			else
+				printk ("ide-tape: Handling our own (not buffer cache originated) WRITE request\n");
+#endif /* IDETAPE_DEBUG */			
+
+			status.all=IN_BYTE (IDETAPE_STATUS_REG);
+			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
+#if IDETAPE_DEBUG
+				printk ("ide-tape: DSC != 1 - Postponing write request\n");
+#endif /* IDETAPE_DEBUG */	
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
+				return;
+			}			
+
+			tape->last_written_valid=1;
+			tape->last_written_block=rq->sector;
+			
+			pc=idetape_next_pc_storage (drive);
+
+			idetape_create_write_cmd (pc,rq->current_nr_sectors);
+			
+			pc->buffer=rq->buffer;
+			pc->buffer_size=rq->current_nr_sectors*512;
+			pc->current_position=rq->buffer;
+			pc->request_transfer=rq->current_nr_sectors*512;
+
+			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+			return;
+					
+		case IDETAPE_PACKET_COMMAND_REQUEST_TYPE1:
+		case IDETAPE_PACKET_COMMAND_REQUEST_TYPE2:
+/*
+ *	This should be unnecessary (postponing of a general packet command),
+ *	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
+				printk ("ide-tape: DSC != 1 - Postponing packet command request\n");
+#endif IDETAPE_DEBUG
+				rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE2;	/* Note that we are waiting for DSC *before* we */
+										/* even issued the command */
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
+				return;
+			}
+			rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+			pc=(idetape_packet_command_t *) rq->buffer;
+			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+			return;
+
+		default:
+			printk ("ide-tape: Unknown command in request - Aborting request\n");
+			idetape_end_request (0,HWGROUP (drive));
+	}	
+}
+
+/*
+ *	idetape_queue_pc_tail is based on the following functions:
+ *
+ *	ide_do_drive_cmd from ide.c
+ *	cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c
+ *
+ *	We add a special packet command request to the tail of the request queue,
+ *	and wait for it to be serviced.
+ *
+ *	This is not to be called from within the request handling part
+ *	of the driver ! We allocate here data in the stack, and it is valid
+ *	until the request is finished. This is not the case for the bottom
+ *	part of the driver, where we are always leaving the functions to wait
+ *	for an interrupt or a timer event.
+ *
+ *	From the bottom part of the driver, we should allocate safe memory
+ *	using idetape_next_pc_storage and idetape_next_rq_storage, and add
+ *	the request to the request list without waiting for it to be serviced !
+ *	In that case, we usually use idetape_queue_pc_head.
+ */
+
+int idetape_queue_pc_tail (ide_drive_t *drive,idetape_packet_command_t *pc)
+{
+	struct request rq;
+
+	ide_init_drive_cmd (&rq);
+	rq.buffer = (char *) pc;
+	rq.cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *	idetape_queue_pc_head generates a new packet command request in front
+ *	of the request queue, before the current request, so that it will be
+ *	processed immediately, on the next pass through the driver.
+ *
+ *	idetape_queue_pc_head is called from the request handling part of
+ *	the driver (the "bottom" part). Safe storage for the request should
+ *	be allocated with idetape_next_pc_storage and idetape_next_rq_storage
+ *	before calling idetape_queue_pc_head.
+ *
+ *	Memory for those requests is pre-allocated at initialization time, and
+ *	is limited to IDETAPE_PC_STACK requests. We assume that we have enough
+ *	space for the maximum possible number of inter-dependent packet commands.
+ *
+ *	The higher level of the driver - The ioctl handler and the character
+ *	device handling functions should queue request to the lower level part
+ *	and wait for their completion using idetape_queue_pc_tail or
+ *	idetape_queue_rw_tail.
+ */
+ 
+void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq)
+
+{
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+/*
+ *	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.
+ */
+
+int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+	struct request rq;
+
+#if IDETAPE_DEBUG
+	printk ("idetape_queue_rw_tail: cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG */
+	/* build up a special read request, and add it to the queue */
+	
+	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;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *	Copied from ide.c (declared static there)
+ */
+
+void idetape_fixstring (byte *s, const int bytecount, const int byteswap)
+{
+	byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+	if (byteswap) {
+		/* convert from big-endian to host byte order */
+		for (p = end ; p != s;) {
+			unsigned short *pp = (unsigned short *) (p -= 2);
+			*pp = ntohs(*pp);
+		}
+	}
+
+	/* strip leading blanks */
+	while (s != end && *s == ' ')
+		++s;
+
+	/* compress internal blanks and strip trailing blanks */
+	while (s != end && *s) {
+		if (*s++ != ' ' || (s != end && *s && *s != ' '))
+			*p++ = *(s-1);
+	}
+
+	/* wipe out trailing garbage */
+	while (p != end)
+		*p++ = '\0';
+}
+
+/*
+ *	idetape_zero_packet_command just zeros a packet command and
+ *	sets the number of retries to 0, as we haven't retried it yet.
+ */
+ 
+void idetape_zero_packet_command (idetape_packet_command_t *pc)
+
+{
+	int i;
+	
+	for (i=0;i<12;i++)
+		pc->c[i]=0;
+	pc->retries=0;
+}
+
+/*
+ *	idetape_swap_shorts converts a 16 bit number from little endian
+ *	to big endian format.
+ */
+ 
+unsigned short idetape_swap_short (unsigned short temp)
+
+{
+	union convert {
+		unsigned all	:16;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+		} b;
+	} original,converted;
+	
+	original.all=temp;
+	converted.b.b1=original.b.b2;
+	converted.b.b2=original.b.b1;
+	return (converted.all);
+}
+
+/*
+ *	idetape_swap_long converts from little endian to big endian format.
+ */
+ 
+unsigned long idetape_swap_long (unsigned long temp)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original,converted;
+	
+	original.all=temp;
+	converted.b.b1=original.b.b4;
+	converted.b.b2=original.b.b3;
+	converted.b.b3=original.b.b2;
+	converted.b.b4=original.b.b1;
+	return (converted.all);
+}
+
+
+/*
+ *	idetape_next_pc_storage returns a pointer to a place in which we can
+ *	safely store a packet command, even though we intend to leave the
+ *	driver. A storage space for a maximum of IDETAPE_PC_STACK packet
+ *	commands is allocated at initialization time.
+ */
+ 
+idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+#if IDETAPE_DEBUG
+	printk ("ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
+#endif /* IDETAPE_DEBUG */
+	if (tape->pc_stack_index==IDETAPE_PC_STACK)
+		tape->pc_stack_index=0;
+	return (&(tape->pc_stack [tape->pc_stack_index++]));
+}
+
+/*
+ *	idetape_next_rq_storage is used along with idetape_next_pc_storage.
+ *	Since we queue packet commands in the request queue, we need to
+ *	allocate a request, along with the allocation of a packet command.
+ */
+ 
+/**************************************************************
+ *                                                            *
+ *  This should get fixed to use kmalloc(GFP_ATOMIC, ..)      *
+ *  followed later on by kfree().   -ml                       *
+ *                                                            *
+ **************************************************************/
+ 
+struct request *idetape_next_rq_storage (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
+#endif /* IDETAPE_DEBUG */
+	if (tape->rq_stack_index==IDETAPE_PC_STACK)
+		tape->rq_stack_index=0;
+	return (&(tape->rq_stack [tape->rq_stack_index++]));
+}
+
+/*
+ *	Block device interface functions
+ *
+ *	The default action is not to allow direct access to the block device
+ *	interface (-EBUSY will be returned on open).
+ */
+
+int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+
+{
+#if IDETAPE_ALLOW_OPENING_BLOCK_DEVICE
+	return (0);
+#else
+	printk ("ide-tape: The block device interface should not be used.\n");
+	printk ("ide-tape: Use the character device interfaces\n");
+	printk ("ide-tape: /dev/ht0 and /dev/nht0 instead.\n");
+	printk ("ide-tape: (Run linux/drivers/block/MAKEDEV.ide to create them)\n");
+	printk ("ide-tape: Refusing open request.\n");
+	return (-EBUSY);
+#endif /* IDETAPE_ALLOW_OPENING_BLOCK_DEVICE */
+}
+
+void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+
+{
+	return;
+}
+
+/*
+ *	Character device interface functions
+ */
+
+/*
+ *	lseek is currently not installed.
+ */
+ 
+int idetape_chrdev_lseek (struct inode *inode, struct file *file, off_t offset, int origin)
+
+{
+	ide_drive_t *drive;
+	
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_lseek\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	if (idetape_position_tape (drive,offset) != 0) {
+		printk ("ide-tape: Rewinding tape failed\n");
+		return (-1);
+	}
+	
+	return (0);
+}
+
+/*
+ *	Our character device read / write functions.
+ *
+ *	The tape is optimized to maximize throughpot 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.
+ *
+ *	Again, for best results use an integral number of the tape's parameter
+ *	(which is displayed in the driver installation stage). I will soon
+ *	add an ioctl to get this important parameter.
+ */
+
+/*
+ *	Our character device read function.
+ */
+
+int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	int blocks,remainder,retval,ctl_bytes;
+	char *buf_ptr;
+	unsigned long previous_block_address,actually_read;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_read\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+	tape->last_dt_was_write=0;
+
+	if (count==0)
+		return (0);
+
+	actually_read=0;
+	buf_ptr=buf;
+	ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
+	blocks=count/ctl_bytes;
+	remainder=count%ctl_bytes;
+	
+	while (blocks) {
+#if IDETAPE_DEBUG
+		printk ("Adding a READ request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+		previous_block_address=tape->block_address;
+		retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,tape->capabilities.ctl,tape->data_buffer);
+		actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
+
+		if (retval) {
+			printk ("ide-tape: Error occured while reading\n");
+			return (actually_read);
+		}
+#if IDETAPE_DEBUG
+	printk ("Copying %d bytes to the user space memory\n",ctl_bytes);
+#endif /* IDETAPE_DEBUG */
+
+		memcpy_tofs (buf_ptr,tape->data_buffer,ctl_bytes);
+		buf_ptr+=ctl_bytes;
+		blocks--;
+	}
+	if (remainder)
+		return (actually_read+idetape_chrdev_read_remainder (inode,file,buf_ptr,remainder));
+	else
+		return (actually_read);		
+}
+ 
+int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	int blocks,remainder,retval;
+	unsigned long previous_block_address,actually_read;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_read_remainder\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+
+	tape->last_dt_was_write=0;
+
+	if (count==0)
+		return (0);
+
+
+	blocks=count/512;
+	remainder=count%512;
+	if (remainder) {
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Padding read to block boundary\n");
+#endif /* IDETAPE_DEBUG */
+		blocks++;
+	}
+#if IDETAPE_DEBUG
+	printk ("Adding a READ request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+	previous_block_address=tape->block_address;
+	retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,tape->data_buffer);
+	if (retval) {
+		printk ("ide-tape: Error occured while reading\n");
+		actually_read=512*(tape->block_address-previous_block_address);
+		if (actually_read > count)
+			actually_read=count;
+		if (actually_read != 0)
+			memcpy_tofs (buf,tape->data_buffer,actually_read);
+		return (actually_read);
+	}
+#if IDETAPE_DEBUG
+	printk ("Copying %d bytes to the user space memory\n",count);
+#endif /* IDETAPE_DEBUG */
+	memcpy_tofs (buf,tape->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,ctl_bytes;
+	const char *buf_ptr;
+	unsigned long previous_block_address,actually_written;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_write\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+	tape->last_dt_was_write=1;
+
+	if (count==0)
+		return (0);
+
+	actually_written=0;
+	buf_ptr=buf;
+	ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
+	blocks=count/ctl_bytes;
+	remainder=count%ctl_bytes;
+
+	while (blocks) {
+#if IDETAPE_DEBUG
+		printk ("Copying %d bytes from the user space memory\n",ctl_bytes);
+#endif /* IDETAPE_DEBUG */
+		memcpy_fromfs (tape->data_buffer,buf_ptr,ctl_bytes);
+		buf_ptr+=ctl_bytes;
+#if IDETAPE_DEBUG
+		printk ("Adding a WRITE request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+		previous_block_address=tape->block_address;
+		retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,tape->capabilities.ctl,tape->data_buffer);
+		actually_written+=tape->tape_block_size*(tape->block_address-previous_block_address);
+
+		if (retval) {
+			printk ("ide-tape: Error occured while writing\n");
+			return (actually_written);
+		}
+		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
+	printk ("Reached idetape_chrdev_write_remainder\n");
+#endif /* IDETAPE_DEBUG */
+		
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+
+	blocks=count/512;
+	remainder=count%512;
+	if (remainder)
+		blocks++;
+#if IDETAPE_DEBUG
+	printk ("Copying %d bytes from the user space memory\n",count);
+#endif /* IDETAPE_DEBUG */
+
+	memcpy_fromfs (tape->data_buffer,buf,count);
+	if (remainder) {
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Padding written data to block boundary\n");
+#endif /* IDETAPE_DEBUG */
+		ptr=tape->data_buffer+(blocks-1)*512;
+		memset (ptr,0,remainder);
+	}
+#if IDETAPE_DEBUG
+	printk ("Adding a WRITE request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+
+	previous_block_address=tape->block_address;
+	retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,blocks,tape->data_buffer);
+	if (retval) {
+		printk ("ide-tape: Error occured while writing\n");
+		actually_written=512*(tape->block_address-previous_block_address);
+		if (actually_written > count)
+			actually_written=count;
+		return (actually_written);
+	}
+	return (count);
+}
+
+/*
+ *	Our character device ioctls.
+ *
+ *	General mtio.h magnetic io commands are supported here, and not in
+ *	the correspoding block interface.
+ *
+ *	Our own ide-tape ioctls are supported on both interfaces.
+ */
+
+int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+
+{
+	struct mtop mtop;
+	ide_drive_t *drive;
+	int retval;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
+#endif
+
+	drive=idetape_chrdev.drive;
+
+	switch (cmd) {
+		case MTIOCTOP:
+			retval=verify_area (VERIFY_READ,(char *) arg,sizeof (struct mtop));
+			if (retval) return (retval);
+			memcpy_fromfs ((char *) &mtop, (char *) arg, sizeof (struct mtop));
+			return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+		default:
+			return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
+	}
+}
+
+/*
+ *	idetape_mtioctop is called from idetape_chrdev_ioctl when
+ *	the general mtio MTIOCTOP ioctl is requested.
+ *
+ *	We currently support the following mtio.h operations:
+ *
+ *	MTFSF	-	Space over mt_count filemarks in the positive direction.
+ *			The tape is positioned after the last spaced filemark.
+ *
+ *	MTFSFM	-	Same as MTFSF, but the tape is positioned before the
+ *			last filemark.
+ *
+ *	MTBSF	-	Steps background over mt_count filemarks, tape is
+ *			positioned before the last filemark.
+ *
+ *	MTBSFM	-	Like MTBSF, only tape is positioned after the last filemark.
+ *
+ *	MTWEOF	-	Writes mt_count filemarks. Tape is positioned after
+ *			the last written filemark.
+ *
+ *	MTREW	-	Rewindes tape.
+ *
+ *	MTNOP	-	Flushes tape buffers.
+ *
+ *	MTEOM	-	Moves to the end of recorded data.
+ *
+ *	MTERASE	-	Erases tape.
+ *
+ *	The following commands are currently not supported:
+ *
+ *	MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTOFFL, MTRETEN, MTSEEK, MTSETBLK,
+ *	MTSETDENSITY, MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ */
+ 
+int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
+
+{
+	int i,retval;
+	
+	idetape_packet_command_t pc;
+
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+
+	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) return (retval);
+
+	switch (mt_op) {
+		case MTFSF:
+#if IDETAPE_DEBUG
+			printk ("Handling MTFSF command\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_space_cmd (&pc,mt_count,IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTFSFM:
+#if IDETAPE_DEBUG
+			printk ("Handling MTFSFM command\n");
+#endif /* IDETAPE_DEBUG */
+			retval=idetape_mtioctop (drive,MTFSF,mt_count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTBSF,1));
+		case MTBSF:
+#if IDETAPE_DEBUG
+			printk ("Handling MTBSF command\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_space_cmd (&pc,-mt_count,IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTBSFM:
+#if IDETAPE_DEBUG
+			printk ("Handling MTBSFM command\n");
+#endif /* IDETAPE_DEBUG */
+			retval=idetape_mtioctop (drive,MTBSF,mt_count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTFSF,1));
+		case MTWEOF:
+#if IDETAPE_DEBUG
+			printk ("Handling MTWEOF command\n");
+#endif /* IDETAPE_DEBUG */
+		
+			for (i=0;i<mt_count;i++) {
+				idetape_create_write_filemark_cmd (&pc,1);
+				retval=idetape_queue_pc_tail (drive,&pc);
+				if (retval) return (retval);
+			}
+			return (0);
+		case MTREW:
+#if IDETAPE_DEBUG
+			printk ("Handling MTREW command\n");
+#endif /* IDETAPE_DEBUG */
+			return (idetape_rewind_tape (drive));
+		case MTNOP:
+#if IDETAPE_DEBUG
+			printk ("Handling MTNOP command\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_write_filemark_cmd (&pc,0);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTEOM:
+#if IDETAPE_DEBUG
+			printk ("Handling MTEOM command\n");
+#endif /* IDETAPE_DEBUG */
+		
+			idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTERASE:
+#if IDETAPE_DEBUG
+			printk ("Handling MTERASE command\n");
+#endif /* IDETAPE_DEBUG */
+			retval=idetape_position_tape (drive,0);
+			if (retval) return (retval);
+			idetape_create_erase_cmd (&pc);
+			return (idetape_queue_pc_tail (drive,&pc));
+		default:
+			printk ("ide-tape: MTIO operation %d not supported\n",mt_op);
+			return (-EIO);
+	}
+}
+
+/*
+ *	Our character device open function.
+ */
+
+int idetape_chrdev_open (struct inode *inode, struct file *filp)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	unsigned long flags;
+	unsigned int minor;
+		
+	save_flags (flags);
+	cli();
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_open\n");
+#endif /* IDETAPE_DEBUG */
+
+
+	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);
+	}
+
+	if (tape->busy) {
+		restore_flags (flags);		/* Allowing access only through one */
+		return (-EBUSY);		/* one file descriptor */
+	}
+
+	tape->busy=1;
+	restore_flags (flags);
+
+	if (!tape->block_address_valid) {
+		if (idetape_rewind_tape (drive)) {
+			printk ("ide-tape: Rewinding tape failed\n");
+			tape->busy=0;
+			return (-EIO);
+		}
+	}
+
+	tape->last_dt_was_write=0;
+	
+	return (0);
+}
+
+/*
+ *	Our character device release function.
+ */
+
+void idetape_chrdev_release (struct inode *inode, struct file *filp)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	
+	unsigned int minor;
+	idetape_packet_command_t pc;
+	unsigned long flags;
+			
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_release\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+	minor=MINOR (inode->i_rdev);
+
+	if (tape->last_dt_was_write) {
+		idetape_create_write_filemark_cmd (&pc,1);	/* Write a filemark */
+		if (idetape_queue_pc_tail (drive,&pc)) {
+			printk ("ide-tape: Couldn't write a filemark\n");
+			/* ??? */
+		}
+	}
+	else {
+		idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
+		if (idetape_queue_pc_tail (drive,&pc)) {
+			printk ("ide-tape: Couldn't flush buffers\n");
+			/* ??? */
+		}
+	}
+
+	if (minor < 128) {
+		if (idetape_rewind_tape (drive)) {
+			printk ("ide-tape: Rewinding tape failed\n");
+			/* ??? */
+		}
+	}
+
+	save_flags (flags);
+	cli();
+	tape->busy=0;
+	restore_flags (flags);
+		
+	return;
+}
+
+/*
+ *	idetape_position_tape positions the tape to the requested block
+ *	using the LOCATE packet command. A READ POSITION command is then
+ *	issued to check where we are positioned.
+ *
+ *	Like all higher level operations, we queue the commands at the tail
+ *	of the request queue and wait for their completion.
+ *	
+ */
+ 
+int idetape_position_tape (ide_drive_t *drive,unsigned long block)
+
+{
+	int retval;
+	idetape_packet_command_t pc;
+
+	idetape_create_locate_cmd (&pc,block,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));
+}
+
+/*
+ *	Rewinds the tape to the Begining Of the current Partition (BOP).
+ *
+ *	We currently support only one partition.
+ */ 
+
+int idetape_rewind_tape (ide_drive_t *drive)
+
+{
+	int retval;
+	idetape_packet_command_t pc;
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_rewind_tape\n");
+#endif /* IDETAPE_DEBUG */	
+
+	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) return (retval);
+	
+	idetape_create_rewind_cmd (&pc);
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) 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));
+}

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