patch-2.3.17 linux/drivers/scsi/st.c
Next file: linux/drivers/scsi/st.h
Previous file: linux/drivers/scsi/sr_vendor.c
Back to the patch index
Back to the overall index
- Lines: 6630
- Date:
Sat Sep 4 10:48:46 1999
- Orig file:
v2.3.16/linux/drivers/scsi/st.c
- Orig date:
Tue Aug 31 17:29:14 1999
diff -u --recursive --new-file v2.3.16/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
@@ -1,19 +1,19 @@
/*
- SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
- file README.st for more information.
+ SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+ file README.st for more information.
- History:
- Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
- Contribution and ideas from several people including (in alphabetical
- order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
- Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
-
- Copyright 1992 - 1999 Kai Makisara
- email Kai.Makisara@metla.fi
-
- Last modified: Sat Aug 7 13:54:31 1999 by makisara@kai.makisara.local
- Some small formal changes - aeb, 950809
-*/
+ History:
+ Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
+ Contribution and ideas from several people including (in alphabetical
+ order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
+ Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+
+ Copyright 1992 - 1999 Kai Makisara
+ email Kai.Makisara@metla.fi
+
+ Last modified: Sat Aug 7 13:54:31 1999 by makisara@kai.makisara.local
+ Some small formal changes - aeb, 950809
+ */
#include <linux/module.h>
@@ -67,14 +67,25 @@
MODULE_PARM(max_buffers, "i");
MODULE_PARM(max_sg_segs, "i");
#else
+
static struct st_dev_parm {
- char *name;
- int *val;
+ char *name;
+ int *val;
} parms[] __initdata = {
- {"buffer_kbs", &buffer_kbs},
- {"write_threshold_kbs", &write_threshold_kbs},
- {"max_buffers", &max_buffers},
- {"max_sg_segs", &max_sg_segs}};
+ {
+ "buffer_kbs", &buffer_kbs
+ },
+ {
+ "write_threshold_kbs", &write_threshold_kbs
+ },
+ {
+ "max_buffers", &max_buffers
+ },
+ {
+ "max_sg_segs", &max_sg_segs
+ }
+};
+
#endif
@@ -115,7 +126,7 @@
static int st_max_buffers = ST_MAX_BUFFERS;
static int st_max_sg_segs = ST_MAX_SG;
-static Scsi_Tape * scsi_tapes = NULL;
+static Scsi_Tape *scsi_tapes = NULL;
static int modes_defined = FALSE;
@@ -130,142 +141,137 @@
static int st_detect(Scsi_Device *);
static void st_detach(Scsi_Device *);
-struct Scsi_Device_Template st_template = {NULL, "tape", "st", NULL, TYPE_TAPE,
- SCSI_TAPE_MAJOR, 0, 0, 0, 0,
- st_detect, st_init,
- NULL, st_attach, st_detach};
+struct Scsi_Device_Template st_template =
+{NULL, "tape", "st", NULL, TYPE_TAPE,
+ SCSI_TAPE_MAJOR, 0, 0, 0, 0,
+ st_detect, st_init,
+ NULL, st_attach, st_detach};
static int st_compression(Scsi_Tape *, int);
static int find_partition(struct inode *);
static int update_partition(struct inode *);
-static int st_int_ioctl(struct inode * inode, unsigned int cmd_in,
+static int st_int_ioctl(struct inode *inode, unsigned int cmd_in,
unsigned long arg);
+
-
/* Convert the result to success code */
- static int
-st_chk_result(Scsi_Cmnd * SCpnt)
+static int st_chk_result(Scsi_Cmnd * SCpnt)
{
- int dev = TAPE_NR(SCpnt->request.rq_dev);
- int result = SCpnt->result;
- unsigned char * sense = SCpnt->sense_buffer, scode;
-#if DEBUG
- const char *stp;
-#endif
-
- if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
- return 0;
-
- if (driver_byte(result) & DRIVER_SENSE)
- scode = sense[2] & 0x0f;
- else {
- sense[0] = 0; /* We don't have sense data if this byte is zero */
- scode = 0;
- }
-
-#if DEBUG
- if (debugging) {
- printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
- dev, result,
- SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2],
- SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5],
- SCpnt->request_bufflen);
- if (driver_byte(result) & DRIVER_SENSE)
- print_sense("st", SCpnt);
- }
- else
-#endif
- if (!(driver_byte(result) & DRIVER_SENSE) ||
- ((sense[0] & 0x70) == 0x70 &&
- scode != NO_SENSE &&
- scode != RECOVERED_ERROR &&
-/* scode != UNIT_ATTENTION && */
- scode != BLANK_CHECK &&
- scode != VOLUME_OVERFLOW &&
- SCpnt->data_cmnd[0] != MODE_SENSE &&
- SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
- if (driver_byte(result) & DRIVER_SENSE) {
- printk(KERN_WARNING "st%d: Error with sense data: ", dev);
- print_sense("st", SCpnt);
- }
- else
- printk(KERN_WARNING
- "st%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
- dev, result, suggestion(result), driver_byte(result) & DRIVER_MASK,
- host_byte(result));
- }
+ int dev = TAPE_NR(SCpnt->request.rq_dev);
+ int result = SCpnt->result;
+ unsigned char *sense = SCpnt->sense_buffer, scode;
+#if DEBUG
+ const char *stp;
+#endif
+
+ if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
+ return 0;
+
+ if (driver_byte(result) & DRIVER_SENSE)
+ scode = sense[2] & 0x0f;
+ else {
+ sense[0] = 0; /* We don't have sense data if this byte is zero */
+ scode = 0;
+ }
- if ((sense[0] & 0x70) == 0x70 &&
- scode == RECOVERED_ERROR
+#if DEBUG
+ if (debugging) {
+ printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
+ dev, result,
+ SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2],
+ SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5],
+ SCpnt->request_bufflen);
+ if (driver_byte(result) & DRIVER_SENSE)
+ print_sense("st", SCpnt);
+ } else
+#endif
+ if (!(driver_byte(result) & DRIVER_SENSE) ||
+ ((sense[0] & 0x70) == 0x70 &&
+ scode != NO_SENSE &&
+ scode != RECOVERED_ERROR &&
+/* scode != UNIT_ATTENTION && */
+ scode != BLANK_CHECK &&
+ scode != VOLUME_OVERFLOW &&
+ SCpnt->data_cmnd[0] != MODE_SENSE &&
+ SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
+ if (driver_byte(result) & DRIVER_SENSE) {
+ printk(KERN_WARNING "st%d: Error with sense data: ", dev);
+ print_sense("st", SCpnt);
+ } else
+ printk(KERN_WARNING
+ "st%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
+ dev, result, suggestion(result), driver_byte(result) & DRIVER_MASK,
+ host_byte(result));
+ }
+ if ((sense[0] & 0x70) == 0x70 &&
+ scode == RECOVERED_ERROR
#if ST_RECOVERED_WRITE_FATAL
- && SCpnt->data_cmnd[0] != WRITE_6
- && SCpnt->data_cmnd[0] != WRITE_FILEMARKS
+ && SCpnt->data_cmnd[0] != WRITE_6
+ && SCpnt->data_cmnd[0] != WRITE_FILEMARKS
#endif
- ) {
- scsi_tapes[dev].recover_count++;
- scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
-#if DEBUG
- if (debugging) {
- if (SCpnt->data_cmnd[0] == READ_6)
- stp = "read";
- else if (SCpnt->data_cmnd[0] == WRITE_6)
- stp = "write";
- else
- stp = "ioctl";
- printk(ST_DEB_MSG "st%d: Recovered %s error (%d).\n", dev, stp,
- scsi_tapes[dev].recover_count);
- }
-#endif
- if ((sense[2] & 0xe0) == 0)
- return 0;
- }
- return (-EIO);
+ ) {
+ scsi_tapes[dev].recover_count++;
+ scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
+#if DEBUG
+ if (debugging) {
+ if (SCpnt->data_cmnd[0] == READ_6)
+ stp = "read";
+ else if (SCpnt->data_cmnd[0] == WRITE_6)
+ stp = "write";
+ else
+ stp = "ioctl";
+ printk(ST_DEB_MSG "st%d: Recovered %s error (%d).\n", dev, stp,
+ scsi_tapes[dev].recover_count);
+ }
+#endif
+ if ((sense[2] & 0xe0) == 0)
+ return 0;
+ }
+ return (-EIO);
}
/* Wakeup from interrupt */
- static void
-st_sleep_done (Scsi_Cmnd * SCpnt)
+static void st_sleep_done(Scsi_Cmnd * SCpnt)
{
- unsigned int st_nbr;
- int remainder;
- Scsi_Tape * STp;
-
- if ((st_nbr = TAPE_NR(SCpnt->request.rq_dev)) < st_template.nr_dev) {
- STp = &(scsi_tapes[st_nbr]);
- if ((STp->buffer)->writing &&
- (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x40)) {
- /* EOM at write-behind, has all been written? */
- if ((SCpnt->sense_buffer[0] & 0x80) != 0)
- remainder = (SCpnt->sense_buffer[3] << 24) |
- (SCpnt->sense_buffer[4] << 16) |
- (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
- else
- remainder = 0;
- if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW ||
- remainder > 0)
- (STp->buffer)->last_result = SCpnt->result; /* Error */
- else
- (STp->buffer)->last_result = INT_MAX; /* OK */
- }
- else
- (STp->buffer)->last_result = SCpnt->result;
- SCpnt->request.rq_status = RQ_SCSI_DONE;
- (STp->buffer)->last_SCpnt = SCpnt;
+ unsigned int st_nbr;
+ int remainder;
+ Scsi_Tape *STp;
+
+ if ((st_nbr = TAPE_NR(SCpnt->request.rq_dev)) < st_template.nr_dev) {
+ STp = &(scsi_tapes[st_nbr]);
+ if ((STp->buffer)->writing &&
+ (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x40)) {
+ /* EOM at write-behind, has all been written? */
+ if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+ remainder = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+ else
+ remainder = 0;
+ if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW ||
+ remainder > 0)
+ (STp->buffer)->last_result = SCpnt->result; /* Error */
+ else
+ (STp->buffer)->last_result = INT_MAX; /* OK */
+ } else
+ (STp->buffer)->last_result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_SCSI_DONE;
+ (STp->buffer)->last_SCpnt = SCpnt;
#if DEBUG
- STp->write_pending = 0;
+ STp->write_pending = 0;
#endif
- up(SCpnt->request.sem);
- }
+ up(SCpnt->request.sem);
+ }
#if DEBUG
- else if (debugging)
- printk(KERN_ERR "st?: Illegal interrupt device %x\n", st_nbr);
+ else if (debugging)
+ printk(KERN_ERR "st?: Illegal interrupt device %x\n", st_nbr);
#endif
}
@@ -273,2462 +279,2351 @@
/* Do the scsi command. Waits until command performed if do_wait is true.
Otherwise write_behind_check() is used to check that the command
has finished. */
- static Scsi_Cmnd *
-st_do_scsi(Scsi_Cmnd *SCpnt, Scsi_Tape *STp, unsigned char *cmd, int bytes,
- int timeout, int retries, int do_wait)
-{
- unsigned long flags;
- unsigned char *bp;
-
- spin_lock_irqsave(&io_request_lock, flags);
- if (SCpnt == NULL)
- if ((SCpnt = scsi_allocate_device(NULL, STp->device, 1)) == NULL) {
- printk(KERN_ERR "st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt));
- spin_unlock_irqrestore(&io_request_lock, flags);
- return NULL;
- }
-
- cmd[1] |= (SCpnt->lun << 5) & 0xe0;
- init_MUTEX_LOCKED(&STp->sem);
- SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ?
- (STp->buffer)->use_sg : 0;
- if (SCpnt->use_sg) {
- bp = (char *)&((STp->buffer)->sg[0]);
- if ((STp->buffer)->sg_segs < SCpnt->use_sg)
- SCpnt->use_sg = (STp->buffer)->sg_segs;
- }
- else
- bp = (STp->buffer)->b_data;
- SCpnt->cmd_len = 0;
- SCpnt->request.sem = &(STp->sem);
- SCpnt->request.rq_status = RQ_SCSI_BUSY;
- SCpnt->request.rq_dev = STp->devt;
-
- scsi_do_cmd(SCpnt, (void *)cmd, bp, bytes,
- st_sleep_done, timeout, retries);
- spin_unlock_irqrestore(&io_request_lock, flags);
-
- if (do_wait) {
- down(SCpnt->request.sem);
+static Scsi_Cmnd *
+ st_do_scsi(Scsi_Cmnd * SCpnt, Scsi_Tape * STp, unsigned char *cmd, int bytes,
+ int timeout, int retries, int do_wait)
+{
+ unsigned long flags;
+ unsigned char *bp;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ if (SCpnt == NULL)
+ if ((SCpnt = scsi_allocate_device(NULL, STp->device, 1)) == NULL) {
+ printk(KERN_ERR "st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt));
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return NULL;
+ }
+ cmd[1] |= (SCpnt->lun << 5) & 0xe0;
+ init_MUTEX_LOCKED(&STp->sem);
+ SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ?
+ (STp->buffer)->use_sg : 0;
+ if (SCpnt->use_sg) {
+ bp = (char *) &((STp->buffer)->sg[0]);
+ if ((STp->buffer)->sg_segs < SCpnt->use_sg)
+ SCpnt->use_sg = (STp->buffer)->sg_segs;
+ } else
+ bp = (STp->buffer)->b_data;
+ SCpnt->cmd_len = 0;
+ SCpnt->request.sem = &(STp->sem);
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.rq_dev = STp->devt;
+
+ scsi_do_cmd(SCpnt, (void *) cmd, bp, bytes,
+ st_sleep_done, timeout, retries);
+ spin_unlock_irqrestore(&io_request_lock, flags);
- (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
- }
+ if (do_wait) {
+ down(SCpnt->request.sem);
- return SCpnt;
+ (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
+ }
+ return SCpnt;
}
/* Handle the write-behind checking (downs the semaphore) */
- static void
-write_behind_check(Scsi_Tape *STp)
+static void write_behind_check(Scsi_Tape * STp)
{
- ST_buffer * STbuffer;
- ST_partstat * STps;
+ ST_buffer *STbuffer;
+ ST_partstat *STps;
- STbuffer = STp->buffer;
+ STbuffer = STp->buffer;
#if DEBUG
- if (STp->write_pending)
- STp->nbr_waits++;
- else
- STp->nbr_finished++;
+ if (STp->write_pending)
+ STp->nbr_waits++;
+ else
+ STp->nbr_finished++;
#endif
- down(&(STp->sem));
+ down(&(STp->sem));
- (STp->buffer)->last_result_fatal = st_chk_result((STp->buffer)->last_SCpnt);
- scsi_release_command((STp->buffer)->last_SCpnt);
+ (STp->buffer)->last_result_fatal = st_chk_result((STp->buffer)->last_SCpnt);
+ scsi_release_command((STp->buffer)->last_SCpnt);
- if (STbuffer->writing < STbuffer->buffer_bytes)
+ if (STbuffer->writing < STbuffer->buffer_bytes)
#if 0
- memcpy(STbuffer->b_data,
- STbuffer->b_data + STbuffer->writing,
- STbuffer->buffer_bytes - STbuffer->writing);
+ memcpy(STbuffer->b_data,
+ STbuffer->b_data + STbuffer->writing,
+ STbuffer->buffer_bytes - STbuffer->writing);
#else
- printk(KERN_WARNING "st: write_behind_check: something left in buffer!\n");
+ printk(KERN_WARNING "st: write_behind_check: something left in buffer!\n");
#endif
- STbuffer->buffer_bytes -= STbuffer->writing;
- STps = &(STp->ps[STp->partition]);
- if (STps->drv_block >= 0) {
- if (STp->block_size == 0)
- STps->drv_block++;
- else
- STps->drv_block += STbuffer->writing / STp->block_size;
- }
- STbuffer->writing = 0;
+ STbuffer->buffer_bytes -= STbuffer->writing;
+ STps = &(STp->ps[STp->partition]);
+ if (STps->drv_block >= 0) {
+ if (STp->block_size == 0)
+ STps->drv_block++;
+ else
+ STps->drv_block += STbuffer->writing / STp->block_size;
+ }
+ STbuffer->writing = 0;
- return;
+ return;
}
/* Step over EOF if it has been inadvertently crossed (ioctl not used because
it messes up the block number). */
- static int
-cross_eof(Scsi_Tape *STp, int forward)
+static int cross_eof(Scsi_Tape * STp, int forward)
{
- Scsi_Cmnd *SCpnt;
- unsigned char cmd[10];
+ Scsi_Cmnd *SCpnt;
+ unsigned char cmd[10];
- cmd[0] = SPACE;
- cmd[1] = 0x01; /* Space FileMarks */
- if (forward) {
- cmd[2] = cmd[3] = 0;
- cmd[4] = 1;
- }
- else
- cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */
- cmd[5] = 0;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n",
- TAPE_NR(STp->devt), forward ? "forward" : "backward");
-#endif
-
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE);
- if (!SCpnt)
- return (-EBUSY);
-
- scsi_release_command(SCpnt);
- SCpnt = NULL;
-
- if ((STp->buffer)->last_result != 0)
- printk(KERN_ERR "st%d: Stepping over filemark %s failed.\n",
- TAPE_NR(STp->devt), forward ? "forward" : "backward");
-
- return (STp->buffer)->last_result_fatal;
-}
+ cmd[0] = SPACE;
+ cmd[1] = 0x01; /* Space FileMarks */
+ if (forward) {
+ cmd[2] = cmd[3] = 0;
+ cmd[4] = 1;
+ } else
+ cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */
+ cmd[5] = 0;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n",
+ TAPE_NR(STp->devt), forward ? "forward" : "backward");
+#endif
+ SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE);
+ if (!SCpnt)
+ return (-EBUSY);
-/* Flush the write buffer (never need to write if variable blocksize). */
- static int
-flush_write_buffer(Scsi_Tape *STp)
-{
- int offset, transfer, blks;
- int result;
- unsigned char cmd[10];
- Scsi_Cmnd *SCpnt;
- ST_partstat * STps;
-
- if ((STp->buffer)->writing) {
- write_behind_check(STp);
- if ((STp->buffer)->last_result_fatal) {
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Async write error (flush) %x.\n",
- TAPE_NR(STp->devt), (STp->buffer)->last_result);
-#endif
- if ((STp->buffer)->last_result == INT_MAX)
- return (-ENOSPC);
- return (-EIO);
- }
- }
-
- if (STp->block_size == 0)
- return 0;
-
- result = 0;
- if (STp->dirty == 1) {
-
- offset = (STp->buffer)->buffer_bytes;
- transfer = ((offset + STp->block_size - 1) /
- STp->block_size) * STp->block_size;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Flushing %d bytes.\n", TAPE_NR(STp->devt), transfer);
-#endif
- memset((STp->buffer)->b_data + offset, 0, transfer - offset);
-
- memset(cmd, 0, 10);
- cmd[0] = WRITE_6;
- cmd[1] = 1;
- blks = transfer / STp->block_size;
- cmd[2] = blks >> 16;
- cmd[3] = blks >> 8;
- cmd[4] = blks;
-
- SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES,
- TRUE);
- if (!SCpnt)
- return (-EBUSY);
-
- STps = &(STp->ps[STp->partition]);
- if ((STp->buffer)->last_result_fatal != 0) {
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x40) &&
- (SCpnt->sense_buffer[2] & 0x0f) == NO_SENSE) {
- STp->dirty = 0;
- (STp->buffer)->buffer_bytes = 0;
- result = (-ENOSPC);
- }
- else {
- printk(KERN_ERR "st%d: Error on flush.\n", TAPE_NR(STp->devt));
- result = (-EIO);
- }
- STps->drv_block = (-1);
- }
- else {
- if (STps->drv_block >= 0)
- STps->drv_block += blks;
- STp->dirty = 0;
- (STp->buffer)->buffer_bytes = 0;
- }
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- }
- return result;
-}
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ if ((STp->buffer)->last_result != 0)
+ printk(KERN_ERR "st%d: Stepping over filemark %s failed.\n",
+ TAPE_NR(STp->devt), forward ? "forward" : "backward");
-/* Flush the tape buffer. The tape will be positioned correctly unless
- seek_next is true. */
- static int
-flush_buffer(struct inode * inode, struct file * filp, int seek_next)
-{
- int backspace, result;
- Scsi_Tape * STp;
- ST_buffer * STbuffer;
- ST_partstat * STps;
- int dev = TAPE_NR(inode->i_rdev);
-
- STp = &(scsi_tapes[dev]);
- STbuffer = STp->buffer;
-
- /*
- * If there was a bus reset, block further access
- * to this device.
- */
- if( STp->device->was_reset )
- return (-EIO);
-
- if (STp->ready != ST_READY)
- return 0;
-
- STps = &(STp->ps[STp->partition]);
- if (STps->rw == ST_WRITING) /* Writing */
- return flush_write_buffer(STp);
-
- if (STp->block_size == 0)
- return 0;
-
- backspace = ((STp->buffer)->buffer_bytes +
- (STp->buffer)->read_pointer) / STp->block_size -
- ((STp->buffer)->read_pointer + STp->block_size - 1) /
- STp->block_size;
- (STp->buffer)->buffer_bytes = 0;
- (STp->buffer)->read_pointer = 0;
- result = 0;
- if (!seek_next) {
- if (STps->eof == ST_FM_HIT) {
- result = cross_eof(STp, FALSE); /* Back over the EOF hit */
- if (!result)
- STps->eof = ST_NOEOF;
- else {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- }
- }
- if (!result && backspace > 0)
- result = st_int_ioctl(inode, MTBSR, backspace);
- }
- else if (STps->eof == ST_FM_HIT) {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- STps->eof = ST_NOEOF;
- }
-
- return result;
-
-}
-
-/* Set the mode parameters */
- static int
-set_mode_densblk(struct inode * inode, Scsi_Tape *STp, ST_mode *STm)
-{
- int set_it = FALSE;
- unsigned long arg;
- int dev = TAPE_NR(inode->i_rdev);
-
- if (!STp->density_changed &&
- STm->default_density >= 0 &&
- STm->default_density != STp->density) {
- arg = STm->default_density;
- set_it = TRUE;
- }
- else
- arg = STp->density;
- arg <<= MT_ST_DENSITY_SHIFT;
- if (!STp->blksize_changed &&
- STm->default_blksize >= 0 &&
- STm->default_blksize != STp->block_size) {
- arg |= STm->default_blksize;
- set_it = TRUE;
- }
- else
- arg |= STp->block_size;
- if (set_it &&
- st_int_ioctl(inode, SET_DENS_AND_BLK, arg)) {
- printk(KERN_WARNING
- "st%d: Can't set default block size to %d bytes and density %x.\n",
- dev, STm->default_blksize, STm->default_density);
- if (modes_defined)
- return (-EINVAL);
- }
- return 0;
+ return (STp->buffer)->last_result_fatal;
}
-
-/* Open the device */
- static int
-scsi_tape_open(struct inode * inode, struct file * filp)
+
+/* Flush the write buffer (never need to write if variable blocksize). */
+static int flush_write_buffer(Scsi_Tape * STp)
{
- unsigned short flags;
- int i, need_dma_buffer, new_session = FALSE;
- unsigned char cmd[10];
- Scsi_Cmnd * SCpnt;
- Scsi_Tape * STp;
- ST_mode * STm;
- ST_partstat * STps;
- int dev = TAPE_NR(inode->i_rdev);
- int mode = TAPE_MODE(inode->i_rdev);
-
- if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
- return (-ENXIO);
-
- if( !scsi_block_when_processing_errors(scsi_tapes[dev].device) ) {
- return -ENXIO;
- }
-
- STp = &(scsi_tapes[dev]);
- if (STp->in_use) {
-#if DEBUG
- printk(ST_DEB_MSG "st%d: Device already in use.\n", dev);
-#endif
- return (-EBUSY);
- }
- STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
-
- if (mode != STp->current_mode) {
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Mode change from %d to %d.\n",
- dev, STp->current_mode, mode);
-#endif
- new_session = TRUE;
- STp->current_mode = mode;
- }
- STm = &(STp->modes[STp->current_mode]);
-
- /* Allocate a buffer for this user */
- need_dma_buffer = STp->restr_dma;
- for (i=0; i < st_nbr_buffers; i++)
- if (!st_buffers[i]->in_use &&
- (!need_dma_buffer || st_buffers[i]->dma))
- break;
- if (i >= st_nbr_buffers) {
- STp->buffer = new_tape_buffer(FALSE, need_dma_buffer);
- if (STp->buffer == NULL) {
- printk(KERN_WARNING "st%d: Can't allocate tape buffer.\n", dev);
- return (-EBUSY);
- }
- }
- else
- STp->buffer = st_buffers[i];
- (STp->buffer)->in_use = 1;
- (STp->buffer)->writing = 0;
- (STp->buffer)->last_result_fatal = 0;
- (STp->buffer)->use_sg = STp->device->host->sg_tablesize;
-
- /* Compute the usable buffer size for this SCSI adapter */
- if (!(STp->buffer)->use_sg)
- (STp->buffer)->buffer_size = (STp->buffer)->sg[0].length;
- else {
- for (i=0, (STp->buffer)->buffer_size = 0; i < (STp->buffer)->use_sg &&
- i < (STp->buffer)->sg_segs; i++)
- (STp->buffer)->buffer_size += (STp->buffer)->sg[i].length;
- }
-
- flags = filp->f_flags;
- STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
-
- STp->dirty = 0;
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STps = &(STp->ps[i]);
- STps->rw = ST_IDLE;
- }
- STp->ready = ST_READY;
- STp->recover_count = 0;
-#if DEBUG
- STp->nbr_waits = STp->nbr_finished = 0;
-#endif
-
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
- if(st_template.module)
- __MOD_INC_USE_COUNT(st_template.module);
-
- memset ((void *) &cmd[0], 0, 10);
- cmd[0] = TEST_UNIT_READY;
-
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES,
- TRUE);
- if (!SCpnt) {
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
- if(st_template.module)
- __MOD_DEC_USE_COUNT(st_template.module);
- return (-EBUSY);
- }
-
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
- memset ((void *) &cmd[0], 0, 10);
- cmd[0] = TEST_UNIT_READY;
-
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES,
- TRUE);
-
- (STp->device)->was_reset = 0;
- STp->partition = STp->new_partition = 0;
- if (STp->can_partitions)
- STp->nbr_partitions = 1; /* This guess will be updated later if necessary */
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STps = &(STp->ps[i]);
- STps->rw = ST_IDLE;
- STps->eof = ST_NOEOF;
- STps->at_sm = 0;
- STps->last_block_valid = FALSE;
- STps->drv_block = 0;
- STps->drv_file = 0 ;
- }
- new_session = TRUE;
- }
-
- if ((STp->buffer)->last_result_fatal != 0) {
- if ((STp->device)->scsi_level >= SCSI_2 &&
- (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
- SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */
- STp->ready = ST_NO_TAPE;
- } else
- STp->ready = ST_NOT_READY;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- STp->density = 0; /* Clear the erroneous "residue" */
- STp->write_prot = 0;
- STp->block_size = 0;
- STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
- STp->partition = STp->new_partition = 0;
- STp->door_locked = ST_UNLOCKED;
- STp->in_use = 1;
- return 0;
- }
-
- if (STp->omit_blklims)
- STp->min_block = STp->max_block = (-1);
- else {
- memset ((void *) &cmd[0], 0, 10);
- cmd[0] = READ_BLOCK_LIMITS;
-
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES, TRUE);
-
- if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
- STp->max_block = ((STp->buffer)->b_data[1] << 16) |
- ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
- STp->min_block = ((STp->buffer)->b_data[4] << 8) |
- (STp->buffer)->b_data[5];
+ int offset, transfer, blks;
+ int result;
+ unsigned char cmd[10];
+ Scsi_Cmnd *SCpnt;
+ ST_partstat *STps;
+
+ if ((STp->buffer)->writing) {
+ write_behind_check(STp);
+ if ((STp->buffer)->last_result_fatal) {
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Block limits %d - %d bytes.\n", dev, STp->min_block,
- STp->max_block);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Async write error (flush) %x.\n",
+ TAPE_NR(STp->devt), (STp->buffer)->last_result);
#endif
- }
- else {
- STp->min_block = STp->max_block = (-1);
+ if ((STp->buffer)->last_result == INT_MAX)
+ return (-ENOSPC);
+ return (-EIO);
+ }
+ }
+ if (STp->block_size == 0)
+ return 0;
+
+ result = 0;
+ if (STp->dirty == 1) {
+
+ offset = (STp->buffer)->buffer_bytes;
+ transfer = ((offset + STp->block_size - 1) /
+ STp->block_size) * STp->block_size;
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Can't read block limits.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Flushing %d bytes.\n", TAPE_NR(STp->devt), transfer);
#endif
- }
- }
+ memset((STp->buffer)->b_data + offset, 0, transfer - offset);
+
+ memset(cmd, 0, 10);
+ cmd[0] = WRITE_6;
+ cmd[1] = 1;
+ blks = transfer / STp->block_size;
+ cmd[2] = blks >> 16;
+ cmd[3] = blks >> 8;
+ cmd[4] = blks;
+
+ SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES,
+ TRUE);
+ if (!SCpnt)
+ return (-EBUSY);
+
+ STps = &(STp->ps[STp->partition]);
+ if ((STp->buffer)->last_result_fatal != 0) {
+ if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x40) &&
+ (SCpnt->sense_buffer[2] & 0x0f) == NO_SENSE) {
+ STp->dirty = 0;
+ (STp->buffer)->buffer_bytes = 0;
+ result = (-ENOSPC);
+ } else {
+ printk(KERN_ERR "st%d: Error on flush.\n", TAPE_NR(STp->devt));
+ result = (-EIO);
+ }
+ STps->drv_block = (-1);
+ } else {
+ if (STps->drv_block >= 0)
+ STps->drv_block += blks;
+ STp->dirty = 0;
+ (STp->buffer)->buffer_bytes = 0;
+ }
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ }
+ return result;
+}
- memset ((void *) &cmd[0], 0, 10);
- cmd[0] = MODE_SENSE;
- cmd[4] = 12;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES, TRUE);
+/* Flush the tape buffer. The tape will be positioned correctly unless
+ seek_next is true. */
+static int flush_buffer(struct inode *inode, struct file *filp, int seek_next)
+{
+ int backspace, result;
+ Scsi_Tape *STp;
+ ST_buffer *STbuffer;
+ ST_partstat *STps;
+ int dev = TAPE_NR(inode->i_rdev);
+
+ STp = &(scsi_tapes[dev]);
+ STbuffer = STp->buffer;
+
+ /*
+ * If there was a bus reset, block further access
+ * to this device.
+ */
+ if (STp->device->was_reset)
+ return (-EIO);
- if ((STp->buffer)->last_result_fatal != 0) {
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: No Mode Sense.\n", dev);
-#endif
- STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */
- (STp->buffer)->last_result_fatal = 0; /* Prevent error propagation */
- STp->drv_write_prot = 0;
- }
- else {
+ if (STp->ready != ST_READY)
+ return 0;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n",
- dev,
- (STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
- (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]);
-#endif
+ STps = &(STp->ps[STp->partition]);
+ if (STps->rw == ST_WRITING) /* Writing */
+ return flush_write_buffer(STp);
- if ((STp->buffer)->b_data[3] >= 8) {
- STp->drv_buffer = ((STp->buffer)->b_data[2] >> 4) & 7;
- STp->density = (STp->buffer)->b_data[4];
- STp->block_size = (STp->buffer)->b_data[9] * 65536 +
- (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Density %x, tape length: %x, drv buffer: %d\n",
- dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
- (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
- STp->drv_buffer);
-#endif
- }
-
- if (STp->block_size > (STp->buffer)->buffer_size &&
- !enlarge_buffer(STp->buffer, STp->block_size, STp->restr_dma)) {
- printk(KERN_NOTICE "st%d: Blocksize %d too large for buffer.\n", dev,
- STp->block_size);
- scsi_release_command(SCpnt);
- (STp->buffer)->in_use = 0;
- STp->buffer = NULL;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
- if(st_template.module)
- __MOD_DEC_USE_COUNT(st_template.module);
- return (-EIO);
- }
- STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
- }
- scsi_release_command(SCpnt);
- SCpnt = NULL;
-
- if (STp->block_size > 0)
- (STp->buffer)->buffer_blocks = (STp->buffer)->buffer_size / STp->block_size;
- else
- (STp->buffer)->buffer_blocks = 1;
- (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
-
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev,
- STp->block_size, (STp->buffer)->buffer_size,
- (STp->buffer)->buffer_blocks);
-#endif
-
- if (STp->drv_write_prot) {
- STp->write_prot = 1;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Write protected\n", dev);
-#endif
- if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
- (STp->buffer)->in_use = 0;
- STp->buffer = NULL;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
- if(st_template.module)
- __MOD_DEC_USE_COUNT(st_template.module);
- return (-EROFS);
- }
- }
-
- if (STp->can_partitions && STp->nbr_partitions < 1) {
- /* This code is reached when the device is opened for the first time
- after the driver has been initialized with tape in the drive and the
- partition support has been enabled. */
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Updating partition number in status.\n", dev);
-#endif
- if ((STp->partition = find_partition(inode)) < 0) {
- (STp->buffer)->in_use = 0;
- STp->buffer = NULL;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
- if(st_template.module)
- __MOD_DEC_USE_COUNT(st_template.module);
- return STp->partition;
- }
- STp->new_partition = STp->partition;
- STp->nbr_partitions = 1; /* This guess will be updated when necessary */
- }
-
- if (new_session) { /* Change the drive parameters for the new mode */
- STp->density_changed = STp->blksize_changed = FALSE;
- STp->compression_changed = FALSE;
- if (!(STm->defaults_for_writes) &&
- (i = set_mode_densblk(inode, STp, STm)) < 0) {
- (STp->buffer)->in_use = 0;
- STp->buffer = NULL;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
- if(st_template.module)
- __MOD_DEC_USE_COUNT(st_template.module);
- return i;
- }
- if (STp->default_drvbuffer != 0xff) {
- if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer))
- printk(KERN_WARNING "st%d: Can't set default drive buffering to %d.\n",
- dev, STp->default_drvbuffer);
- }
- }
+ if (STp->block_size == 0)
+ return 0;
- STp->in_use = 1;
+ backspace = ((STp->buffer)->buffer_bytes +
+ (STp->buffer)->read_pointer) / STp->block_size -
+ ((STp->buffer)->read_pointer + STp->block_size - 1) /
+ STp->block_size;
+ (STp->buffer)->buffer_bytes = 0;
+ (STp->buffer)->read_pointer = 0;
+ result = 0;
+ if (!seek_next) {
+ if (STps->eof == ST_FM_HIT) {
+ result = cross_eof(STp, FALSE); /* Back over the EOF hit */
+ if (!result)
+ STps->eof = ST_NOEOF;
+ else {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ }
+ }
+ if (!result && backspace > 0)
+ result = st_int_ioctl(inode, MTBSR, backspace);
+ } else if (STps->eof == ST_FM_HIT) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_NOEOF;
+ }
+ return result;
- return 0;
}
-
-/* Flush the tape buffer before close */
- static int
-scsi_tape_flush(struct file * filp)
+/* Set the mode parameters */
+static int set_mode_densblk(struct inode *inode, Scsi_Tape * STp, ST_mode * STm)
{
- int result = 0, result2;
- static unsigned char cmd[10];
- Scsi_Cmnd * SCpnt;
- Scsi_Tape * STp;
- ST_mode * STm;
- ST_partstat * STps;
-
- struct inode *inode = filp->f_dentry->d_inode;
- kdev_t devt = inode->i_rdev;
- int dev;
-
- if (file_count(filp) > 1)
+ int set_it = FALSE;
+ unsigned long arg;
+ int dev = TAPE_NR(inode->i_rdev);
+
+ if (!STp->density_changed &&
+ STm->default_density >= 0 &&
+ STm->default_density != STp->density) {
+ arg = STm->default_density;
+ set_it = TRUE;
+ } else
+ arg = STp->density;
+ arg <<= MT_ST_DENSITY_SHIFT;
+ if (!STp->blksize_changed &&
+ STm->default_blksize >= 0 &&
+ STm->default_blksize != STp->block_size) {
+ arg |= STm->default_blksize;
+ set_it = TRUE;
+ } else
+ arg |= STp->block_size;
+ if (set_it &&
+ st_int_ioctl(inode, SET_DENS_AND_BLK, arg)) {
+ printk(KERN_WARNING
+ "st%d: Can't set default block size to %d bytes and density %x.\n",
+ dev, STm->default_blksize, STm->default_density);
+ if (modes_defined)
+ return (-EINVAL);
+ }
return 0;
+}
+
+
+/* Open the device */
+static int scsi_tape_open(struct inode *inode, struct file *filp)
+{
+ unsigned short flags;
+ int i, need_dma_buffer, new_session = FALSE;
+ unsigned char cmd[10];
+ Scsi_Cmnd *SCpnt;
+ Scsi_Tape *STp;
+ ST_mode *STm;
+ ST_partstat *STps;
+ int dev = TAPE_NR(inode->i_rdev);
+ int mode = TAPE_MODE(inode->i_rdev);
- dev = TAPE_NR(devt);
- STp = &(scsi_tapes[dev]);
- STm = &(STp->modes[STp->current_mode]);
- STps = &(STp->ps[STp->partition]);
+ if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
+ return (-ENXIO);
+
+ if (!scsi_block_when_processing_errors(scsi_tapes[dev].device)) {
+ return -ENXIO;
+ }
+ STp = &(scsi_tapes[dev]);
+ if (STp->in_use) {
+#if DEBUG
+ printk(ST_DEB_MSG "st%d: Device already in use.\n", dev);
+#endif
+ return (-EBUSY);
+ }
+ STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
- if (STp->can_partitions &&
- (result = update_partition(inode)) < 0) {
+ if (mode != STp->current_mode) {
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: update_partition at close failed.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Mode change from %d to %d.\n",
+ dev, STp->current_mode, mode);
#endif
- goto out;
- }
+ new_session = TRUE;
+ STp->current_mode = mode;
+ }
+ STm = &(STp->modes[STp->current_mode]);
- if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) {
+ /* Allocate a buffer for this user */
+ need_dma_buffer = STp->restr_dma;
+ for (i = 0; i < st_nbr_buffers; i++)
+ if (!st_buffers[i]->in_use &&
+ (!need_dma_buffer || st_buffers[i]->dma))
+ break;
+ if (i >= st_nbr_buffers) {
+ STp->buffer = new_tape_buffer(FALSE, need_dma_buffer);
+ if (STp->buffer == NULL) {
+ printk(KERN_WARNING "st%d: Can't allocate tape buffer.\n", dev);
+ return (-EBUSY);
+ }
+ } else
+ STp->buffer = st_buffers[i];
+ (STp->buffer)->in_use = 1;
+ (STp->buffer)->writing = 0;
+ (STp->buffer)->last_result_fatal = 0;
+ (STp->buffer)->use_sg = STp->device->host->sg_tablesize;
+
+ /* Compute the usable buffer size for this SCSI adapter */
+ if (!(STp->buffer)->use_sg)
+ (STp->buffer)->buffer_size = (STp->buffer)->sg[0].length;
+ else {
+ for (i = 0, (STp->buffer)->buffer_size = 0; i < (STp->buffer)->use_sg &&
+ i < (STp->buffer)->sg_segs; i++)
+ (STp->buffer)->buffer_size += (STp->buffer)->sg[i].length;
+ }
- result = flush_write_buffer(STp);
+ flags = filp->f_flags;
+ STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
+ STp->dirty = 0;
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ }
+ STp->ready = ST_READY;
+ STp->recover_count = 0;
#if DEBUG
- if (debugging) {
- printk(ST_DEB_MSG "st%d: File length %ld bytes.\n",
- dev, (long)(filp->f_pos));
- printk(ST_DEB_MSG "st%d: Async write waits %d, finished %d.\n",
- dev, STp->nbr_waits, STp->nbr_finished);
- }
+ STp->nbr_waits = STp->nbr_finished = 0;
#endif
- if (result == 0 || result == (-ENOSPC)) {
+ if (scsi_tapes[dev].device->host->hostt->module)
+ __MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (st_template.module)
+ __MOD_INC_USE_COUNT(st_template.module);
- memset(cmd, 0, 10);
- cmd[0] = WRITE_FILEMARKS;
- cmd[4] = 1 + STp->two_fm;
+ memset((void *) &cmd[0], 0, 10);
+ cmd[0] = TEST_UNIT_READY;
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES,
+ SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES,
TRUE);
- if (!SCpnt)
- goto out;
-
- if ((STp->buffer)->last_result_fatal != 0 &&
- ((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
- (SCpnt->sense_buffer[2] & 0x4f) != 0x40 ||
- ((SCpnt->sense_buffer[0] & 0x80) != 0 &&
- (SCpnt->sense_buffer[3] | SCpnt->sense_buffer[4] |
- SCpnt->sense_buffer[5] |
- SCpnt->sense_buffer[6]) == 0))) {
- /* Filter out successful write at EOM */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
- if (result == 0)
- result = (-EIO);
+ if (!SCpnt) {
+ if (scsi_tapes[dev].device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (st_template.module)
+ __MOD_DEC_USE_COUNT(st_template.module);
+ return (-EBUSY);
}
- else {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- if (STps->drv_file >= 0)
- STps->drv_file++ ;
- STps->drv_block = 0;
- if (STp->two_fm)
- cross_eof(STp, FALSE);
- STps->eof = ST_FM;
- }
- }
-
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Buffer flushed, %d EOF(s) written\n",
- dev, cmd[4]);
-#endif
- }
- else if (!STp->rew_at_close) {
- STps = &(STp->ps[STp->partition]);
- if (!STm->sysv || STps->rw != ST_READING) {
- if (STp->can_bsr)
- result = flush_buffer(inode, filp, 0);
- else if (STps->eof == ST_FM_HIT) {
- result = cross_eof(STp, FALSE);
- if (result) {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- STps->eof = ST_FM;
+ if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+ memset((void *) &cmd[0], 0, 10);
+ cmd[0] = TEST_UNIT_READY;
+
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES,
+ TRUE);
+
+ (STp->device)->was_reset = 0;
+ STp->partition = STp->new_partition = 0;
+ if (STp->can_partitions)
+ STp->nbr_partitions = 1; /* This guess will be updated later if necessary */
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(STp->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = FALSE;
+ STps->drv_block = 0;
+ STps->drv_file = 0;
}
- else
- STps->eof = ST_NOEOF;
- }
+ new_session = TRUE;
}
- else if ((STps->eof == ST_NOEOF &&
- !(result = cross_eof(STp, TRUE))) ||
- STps->eof == ST_FM_HIT) {
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- STps->eof = ST_FM;
- }
- }
-
-out:
- if (STp->rew_at_close) {
- result2 = st_int_ioctl(inode, MTREW, 1);
- if (result == 0)
- result = result2;
- }
-
- return result;
-}
-
-
-/* Close the device and release it */
- static int
-scsi_tape_close(struct inode * inode, struct file * filp)
-{
- int result = 0;
- Scsi_Tape * STp;
-
- kdev_t devt = inode->i_rdev;
- int dev;
-
- dev = TAPE_NR(devt);
- STp = &(scsi_tapes[dev]);
-
- if (STp->door_locked == ST_LOCKED_AUTO)
- st_int_ioctl(inode, MTUNLOCK, 0);
+ if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
+ SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */
+ STp->ready = ST_NO_TAPE;
+ } else
+ STp->ready = ST_NOT_READY;
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ STp->density = 0; /* Clear the erroneous "residue" */
+ STp->write_prot = 0;
+ STp->block_size = 0;
+ STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
+ STp->partition = STp->new_partition = 0;
+ STp->door_locked = ST_UNLOCKED;
+ STp->in_use = 1;
+ return 0;
+ }
+ if (STp->omit_blklims)
+ STp->min_block = STp->max_block = (-1);
+ else {
+ memset((void *) &cmd[0], 0, 10);
+ cmd[0] = READ_BLOCK_LIMITS;
- if (STp->buffer != NULL) {
- normalize_buffer(STp->buffer);
- (STp->buffer)->in_use = 0;
- }
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES, TRUE);
- STp->in_use = 0;
- if (scsi_tapes[dev].device->host->hostt->module)
- __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
- if(st_template.module)
- __MOD_DEC_USE_COUNT(st_template.module);
+ if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
+ STp->max_block = ((STp->buffer)->b_data[1] << 16) |
+ ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
+ STp->min_block = ((STp->buffer)->b_data[4] << 8) |
+ (STp->buffer)->b_data[5];
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Block limits %d - %d bytes.\n", dev, STp->min_block,
+ STp->max_block);
+#endif
+ } else {
+ STp->min_block = STp->max_block = (-1);
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Can't read block limits.\n", dev);
+#endif
+ }
+ }
- return result;
-}
+ memset((void *) &cmd[0], 0, 10);
+ cmd[0] = MODE_SENSE;
+ cmd[4] = 12;
-
-/* Write command */
-static ssize_t
-st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
-{
- struct inode *inode = filp->f_dentry->d_inode;
- ssize_t total;
- ssize_t i, do_count, blks, retval, transfer;
- int write_threshold;
- int doing_write = 0;
- static unsigned char cmd[10];
- const char *b_point;
- Scsi_Cmnd * SCpnt = NULL;
- Scsi_Tape * STp;
- ST_mode * STm;
- ST_partstat * STps;
- int dev = TAPE_NR(inode->i_rdev);
-
- STp = &(scsi_tapes[dev]);
-
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if( !scsi_block_when_processing_errors(STp->device) ) {
- return -ENXIO;
- }
-
- if (ppos != &filp->f_pos) {
- /* "A request was outside the capabilities of the device." */
- return -ENXIO;
- }
-
- if (STp->ready != ST_READY) {
- if (STp->ready == ST_NO_TAPE)
- return (-ENOMEDIUM);
- else
- return (-EIO);
- }
- STm = &(STp->modes[STp->current_mode]);
- if (!STm->defined)
- return (-ENXIO);
- if (count == 0)
- return 0;
-
- /*
- * If there was a bus reset, block further access
- * to this device.
- */
- if( STp->device->was_reset )
- return (-EIO);
-
-#if DEBUG
- if (!STp->in_use) {
- printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
- return (-EIO);
- }
-#endif
-
- /* Write must be integral number of blocks */
- if (STp->block_size != 0 && (count % STp->block_size) != 0) {
- printk(KERN_WARNING "st%d: Write not multiple of tape block size.\n",
- dev);
- return (-EIO);
- }
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES, TRUE);
- if (STp->can_partitions &&
- (retval = update_partition(inode)) < 0)
- return retval;
- STps = &(STp->ps[STp->partition]);
-
- if (STp->write_prot)
- return (-EACCES);
-
- if (STp->block_size == 0 &&
- count > (STp->buffer)->buffer_size &&
- !enlarge_buffer(STp->buffer, count, STp->restr_dma))
- return (-EOVERFLOW);
-
- if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
- !st_int_ioctl(inode, MTLOCK, 0))
- STp->door_locked = ST_LOCKED_AUTO;
-
- if (STps->rw == ST_READING) {
- retval = flush_buffer(inode, filp, 0);
- if (retval)
- return retval;
- STps->rw = ST_WRITING;
- }
- else if (STps->rw != ST_WRITING &&
- STps->drv_file == 0 && STps->drv_block == 0) {
- if ((retval = set_mode_densblk(inode, STp, STm)) < 0)
- return retval;
- if (STm->default_compression != ST_DONT_TOUCH &&
- !(STp->compression_changed)) {
- if (st_compression(STp, (STm->default_compression == ST_YES))) {
- printk(KERN_WARNING "st%d: Can't set default compression.\n",
- dev);
- if (modes_defined)
- return (-EINVAL);
- }
- }
- }
-
- if ((STp->buffer)->writing) {
- write_behind_check(STp);
- if ((STp->buffer)->last_result_fatal) {
+ if ((STp->buffer)->last_result_fatal != 0) {
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n", dev,
- (STp->buffer)->last_result);
-#endif
- if ((STp->buffer)->last_result == INT_MAX)
- STps->eof = ST_EOM_OK;
- else
- STps->eof = ST_EOM_ERROR;
- }
- }
- if (STps->eof == ST_EOM_OK)
- return (-ENOSPC);
- else if (STps->eof == ST_EOM_ERROR)
- return (-EIO);
-
- /* Check the buffer readability in cases where copy_user might catch
- the problems after some tape movement. */
- if (STp->block_size != 0 &&
- (copy_from_user(&i, buf, 1) != 0 ||
- copy_from_user(&i, buf + count - 1, 1) != 0))
- return (-EFAULT);
-
- if (!STm->do_buffer_writes) {
-#if 0
- if (STp->block_size != 0 && (count % STp->block_size) != 0)
- return (-EIO); /* Write must be integral number of blocks */
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: No Mode Sense.\n", dev);
#endif
- write_threshold = 1;
- }
- else
- write_threshold = (STp->buffer)->buffer_blocks * STp->block_size;
- if (!STm->do_async_writes)
- write_threshold--;
-
- total = count;
-
- memset(cmd, 0, 10);
- cmd[0] = WRITE_6;
- cmd[1] = (STp->block_size != 0);
-
- STps->rw = ST_WRITING;
-
- b_point = buf;
- while ((STp->block_size == 0 && !STm->do_async_writes && count > 0) ||
- (STp->block_size != 0 &&
- (STp->buffer)->buffer_bytes + count > write_threshold))
- {
- doing_write = 1;
- if (STp->block_size == 0)
- do_count = count;
- else {
- do_count = (STp->buffer)->buffer_blocks * STp->block_size -
- (STp->buffer)->buffer_bytes;
- if (do_count > count)
- do_count = count;
- }
-
- i = append_to_buffer(b_point, STp->buffer, do_count);
- if (i) {
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- }
- return i;
- }
-
- if (STp->block_size == 0)
- blks = transfer = do_count;
- else {
- blks = (STp->buffer)->buffer_bytes /
- STp->block_size;
- transfer = blks * STp->block_size;
- }
- cmd[2] = blks >> 16;
- cmd[3] = blks >> 8;
- cmd[4] = blks;
-
- SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout,
- MAX_WRITE_RETRIES, TRUE);
- if (!SCpnt)
- return (-EBUSY);
+ STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */
+ (STp->buffer)->last_result_fatal = 0; /* Prevent error propagation */
+ STp->drv_write_prot = 0;
+ } else {
- if ((STp->buffer)->last_result_fatal != 0) {
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Error on write:\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n",
+ dev,
+ (STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
+ (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]);
#endif
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x40)) {
- if (STp->block_size != 0 && (SCpnt->sense_buffer[0] & 0x80) != 0)
- transfer = (SCpnt->sense_buffer[3] << 24) |
- (SCpnt->sense_buffer[4] << 16) |
- (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
- else if (STp->block_size == 0 &&
- (SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW)
- transfer = do_count;
- else
- transfer = 0;
- if (STp->block_size != 0)
- transfer *= STp->block_size;
- if (transfer <= do_count) {
- filp->f_pos += do_count - transfer;
- count -= do_count - transfer;
- if (STps->drv_block >= 0) {
- if (STp->block_size == 0 && transfer < do_count)
- STps->drv_block++;
- else if (STp->block_size != 0)
- STps->drv_block += (do_count - transfer) / STp->block_size;
- }
- STps->eof = ST_EOM_OK;
- retval = (-ENOSPC); /* EOM within current request */
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: EOM with %d bytes unwritten.\n",
- dev, transfer);
-#endif
- }
- else {
- STps->eof = ST_EOM_ERROR;
- STps->drv_block = (-1); /* Too cautious? */
- retval = (-EIO); /* EOM for old data */
+
+ if ((STp->buffer)->b_data[3] >= 8) {
+ STp->drv_buffer = ((STp->buffer)->b_data[2] >> 4) & 7;
+ STp->density = (STp->buffer)->b_data[4];
+ STp->block_size = (STp->buffer)->b_data[9] * 65536 +
+ (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: EOM with lost data.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Density %x, tape length: %x, drv buffer: %d\n",
+ dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
+ (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
+ STp->drv_buffer);
#endif
- }
- }
- else {
- STps->drv_block = (-1); /* Too cautious? */
- retval = (-EIO);
+ }
+ if (STp->block_size > (STp->buffer)->buffer_size &&
+ !enlarge_buffer(STp->buffer, STp->block_size, STp->restr_dma)) {
+ printk(KERN_NOTICE "st%d: Blocksize %d too large for buffer.\n", dev,
+ STp->block_size);
+ scsi_release_command(SCpnt);
+ (STp->buffer)->in_use = 0;
+ STp->buffer = NULL;
+ if (scsi_tapes[dev].device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (st_template.module)
+ __MOD_DEC_USE_COUNT(st_template.module);
+ return (-EIO);
+ }
+ STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
}
-
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- (STp->buffer)->buffer_bytes = 0;
- STp->dirty = 0;
- if (count < total)
- return total - count;
- else
- return retval;
- }
- filp->f_pos += do_count;
- b_point += do_count;
- count -= do_count;
- if (STps->drv_block >= 0) {
- if (STp->block_size == 0)
- STps->drv_block++;
- else
- STps->drv_block += blks;
- }
- (STp->buffer)->buffer_bytes = 0;
- STp->dirty = 0;
- }
- if (count != 0) {
- STp->dirty = 1;
- i = append_to_buffer(b_point, STp->buffer, count);
- if (i) {
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- }
- return i;
- }
- filp->f_pos += count;
- count = 0;
- }
-
- if (doing_write && (STp->buffer)->last_result_fatal != 0) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- return (STp->buffer)->last_result_fatal;
- }
-
- if (STm->do_async_writes &&
- (((STp->buffer)->buffer_bytes >= STp->write_threshold &&
- (STp->buffer)->buffer_bytes >= STp->block_size) ||
- STp->block_size == 0) ) {
- /* Schedule an asynchronous write */
- if (STp->block_size == 0)
- (STp->buffer)->writing = (STp->buffer)->buffer_bytes;
- else
- (STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
- STp->block_size) * STp->block_size;
- STp->dirty = !((STp->buffer)->writing ==
- (STp->buffer)->buffer_bytes);
-
- if (STp->block_size == 0)
- blks = (STp->buffer)->writing;
- else
- blks = (STp->buffer)->writing / STp->block_size;
- cmd[2] = blks >> 16;
- cmd[3] = blks >> 8;
- cmd[4] = blks;
-#if DEBUG
- STp->write_pending = 1;
-#endif
-
- SCpnt = st_do_scsi(SCpnt, STp, cmd, (STp->buffer)->writing, STp->timeout,
- MAX_WRITE_RETRIES, FALSE);
- if (SCpnt == NULL)
- return (-EIO);
- }
- else if (SCpnt != NULL) {
scsi_release_command(SCpnt);
SCpnt = NULL;
- }
- STps->at_sm &= (total == 0);
- if (total > 0)
- STps->eof = ST_NOEOF;
- return( total);
-}
-
-/* Read data from the tape. Returns zero in the normal case, one if the
- eof status has changed, and the negative error code in case of a
- fatal error. Otherwise updates the buffer and the eof state. */
-static long
-read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
-{
- int transfer, blks, bytes;
- static unsigned char cmd[10];
- Scsi_Cmnd *SCpnt;
- Scsi_Tape *STp;
- ST_mode * STm;
- ST_partstat * STps;
- int dev = TAPE_NR(inode->i_rdev);
- int retval = 0;
-
- if (count == 0)
- return 0;
-
- STp = &(scsi_tapes[dev]);
- STm = &(STp->modes[STp->current_mode]);
- STps = &(STp->ps[STp->partition]);
- if (STps->eof == ST_FM_HIT)
- return 1;
+ if (STp->block_size > 0)
+ (STp->buffer)->buffer_blocks = (STp->buffer)->buffer_size / STp->block_size;
+ else
+ (STp->buffer)->buffer_blocks = 1;
+ (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
- memset(cmd, 0, 10);
- cmd[0] = READ_6;
- cmd[1] = (STp->block_size != 0);
- if (STp->block_size == 0)
- blks = bytes = count;
- else {
- if (STm->do_read_ahead) {
- blks = (STp->buffer)->buffer_blocks;
- bytes = blks * STp->block_size;
- }
- else {
- bytes = count;
- if (bytes > (STp->buffer)->buffer_size)
- bytes = (STp->buffer)->buffer_size;
- blks = bytes / STp->block_size;
- bytes = blks * STp->block_size;
- }
- }
- cmd[2] = blks >> 16;
- cmd[3] = blks >> 8;
- cmd[4] = blks;
-
- SCpnt = *aSCpnt;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, STp->timeout, MAX_RETRIES, TRUE);
- *aSCpnt = SCpnt;
- if (!SCpnt)
- return (-EBUSY);
-
- (STp->buffer)->read_pointer = 0;
- STps->at_sm = 0;
-
- /* Something to check */
- if ((STp->buffer)->last_result_fatal) {
- retval = 1;
#if DEBUG
if (debugging)
- printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
- dev,
- SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
- SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
- SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
- SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
-#endif
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
-
- if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
- SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
-
- if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
- /* Compute the residual count */
- if ((SCpnt->sense_buffer[0] & 0x80) != 0)
- transfer = (SCpnt->sense_buffer[3] << 24) |
- (SCpnt->sense_buffer[4] << 16) |
- (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
- else
- transfer = 0;
- if (STp->block_size == 0 &&
- (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
- transfer = bytes;
-
- if (SCpnt->sense_buffer[2] & 0x20) { /* ILI */
- if (STp->block_size == 0) {
- if (transfer <= 0)
- transfer = 0;
- (STp->buffer)->buffer_bytes = bytes - transfer;
- }
- else {
- scsi_release_command(SCpnt);
- SCpnt = *aSCpnt = NULL;
- if (transfer == blks) { /* We did not get anything, error */
- printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
- if (STps->drv_block >= 0)
- STps->drv_block += blks - transfer + 1;
- st_int_ioctl(inode, MTBSR, 1);
- return (-EIO);
- }
- /* We have some data, deliver it */
- (STp->buffer)->buffer_bytes = (blks - transfer) *
- STp->block_size;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG
- "st%d: ILI but enough data received %ld %d.\n",
- dev, count, (STp->buffer)->buffer_bytes);
-#endif
- if (STps->drv_block >= 0)
- STps->drv_block += 1;
- if (st_int_ioctl(inode, MTBSR, 1))
- return (-EIO);
- }
- }
- else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */
- if (STps->eof != ST_FM_HIT)
- STps->eof = ST_FM_HIT;
- else
- STps->eof = ST_EOD_2;
- if (STp->block_size == 0)
- (STp->buffer)->buffer_bytes = 0;
- else
- (STp->buffer)->buffer_bytes =
- bytes - transfer * STp->block_size;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG
- "st%d: EOF detected (%d bytes read).\n",
- dev, (STp->buffer)->buffer_bytes);
+ printk(ST_DEB_MSG "st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev,
+ STp->block_size, (STp->buffer)->buffer_size,
+ (STp->buffer)->buffer_blocks);
#endif
- }
- else if (SCpnt->sense_buffer[2] & 0x40) {
- if (STps->eof == ST_FM)
- STps->eof = ST_EOD_1;
- else
- STps->eof = ST_EOM_OK;
- if (STp->block_size == 0)
- (STp->buffer)->buffer_bytes = bytes - transfer;
- else
- (STp->buffer)->buffer_bytes =
- bytes - transfer * STp->block_size;
+
+ if (STp->drv_write_prot) {
+ STp->write_prot = 1;
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n",
- dev, (STp->buffer)->buffer_bytes);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Write protected\n", dev);
#endif
+ if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
+ (STp->buffer)->in_use = 0;
+ STp->buffer = NULL;
+ if (scsi_tapes[dev].device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (st_template.module)
+ __MOD_DEC_USE_COUNT(st_template.module);
+ return (-EROFS);
}
- } /* end of EOF, EOM, ILI test */
- else { /* nonzero sense key */
+ }
+ if (STp->can_partitions && STp->nbr_partitions < 1) {
+ /* This code is reached when the device is opened for the first time
+ after the driver has been initialized with tape in the drive and the
+ partition support has been enabled. */
#if DEBUG
if (debugging)
- printk(ST_DEB_MSG "st%d: Tape error while reading.\n", dev);
-#endif
- STps->drv_block = (-1);
- if (STps->eof == ST_FM &&
- (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG
- "st%d: Zero returned for first BLANK CHECK after EOF.\n",
- dev);
+ printk(ST_DEB_MSG "st%d: Updating partition number in status.\n", dev);
#endif
- STps->eof = ST_EOD_2; /* First BLANK_CHECK after FM */
+ if ((STp->partition = find_partition(inode)) < 0) {
+ (STp->buffer)->in_use = 0;
+ STp->buffer = NULL;
+ if (scsi_tapes[dev].device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (st_template.module)
+ __MOD_DEC_USE_COUNT(st_template.module);
+ return STp->partition;
}
- else /* Some other extended sense code */
- retval = (-EIO);
- }
- } /* End of extended sense test */
- else { /* Non-extended sense */
- retval = (STp->buffer)->last_result_fatal;
+ STp->new_partition = STp->partition;
+ STp->nbr_partitions = 1; /* This guess will be updated when necessary */
}
+ if (new_session) { /* Change the drive parameters for the new mode */
+ STp->density_changed = STp->blksize_changed = FALSE;
+ STp->compression_changed = FALSE;
+ if (!(STm->defaults_for_writes) &&
+ (i = set_mode_densblk(inode, STp, STm)) < 0) {
+ (STp->buffer)->in_use = 0;
+ STp->buffer = NULL;
+ if (scsi_tapes[dev].device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (st_template.module)
+ __MOD_DEC_USE_COUNT(st_template.module);
+ return i;
+ }
+ if (STp->default_drvbuffer != 0xff) {
+ if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer))
+ printk(KERN_WARNING "st%d: Can't set default drive buffering to %d.\n",
+ dev, STp->default_drvbuffer);
+ }
+ }
+ STp->in_use = 1;
- } /* End of error handling */
- else /* Read successful */
- (STp->buffer)->buffer_bytes = bytes;
-
- if (STps->drv_block >= 0) {
- if (STp->block_size == 0)
- STps->drv_block++;
- else
- STps->drv_block += (STp->buffer)->buffer_bytes / STp->block_size;
- }
-
- return retval;
+ return 0;
}
-
-/* Read command */
-static ssize_t
-st_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+
+/* Flush the tape buffer before close */
+static int scsi_tape_flush(struct file *filp)
{
- struct inode * inode = filp->f_dentry->d_inode;
- ssize_t total;
- ssize_t i, transfer;
- int special;
- Scsi_Cmnd * SCpnt = NULL;
- Scsi_Tape * STp;
- ST_mode * STm;
- ST_partstat * STps;
- int dev = TAPE_NR(inode->i_rdev);
-
- STp = &(scsi_tapes[dev]);
-
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if( !scsi_block_when_processing_errors(STp->device) ) {
- return -ENXIO;
- }
-
- if (ppos != &filp->f_pos) {
- /* "A request was outside the capabilities of the device." */
- return -ENXIO;
- }
-
- if (STp->ready != ST_READY) {
- if (STp->ready == ST_NO_TAPE)
- return (-ENOMEDIUM);
- else
- return (-EIO);
- }
- STm = &(STp->modes[STp->current_mode]);
- if (!STm->defined)
- return (-ENXIO);
-#if DEBUG
- if (!STp->in_use) {
- printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
- return (-EIO);
- }
-#endif
-
- if (STp->can_partitions &&
- (total = update_partition(inode)) < 0)
- return total;
-
- if (STp->block_size == 0 &&
- count > (STp->buffer)->buffer_size &&
- !enlarge_buffer(STp->buffer, count, STp->restr_dma))
- return (-EOVERFLOW);
-
- if (!(STm->do_read_ahead) && STp->block_size != 0 &&
- (count % STp->block_size) != 0)
- return (-EIO); /* Read must be integral number of blocks */
-
- if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
- !st_int_ioctl(inode, MTLOCK, 0))
- STp->door_locked = ST_LOCKED_AUTO;
-
- STps = &(STp->ps[STp->partition]);
- if (STps->rw == ST_WRITING) {
- transfer = flush_buffer(inode, filp, 0);
- if (transfer)
- return transfer;
- STps->rw = ST_READING;
- }
-
-#if DEBUG
- if (debugging && STps->eof != ST_NOEOF)
- printk(ST_DEB_MSG "st%d: EOF/EOM flag up (%d). Bytes %d\n", dev,
- STps->eof, (STp->buffer)->buffer_bytes);
-#endif
- if ((STp->buffer)->buffer_bytes == 0 &&
- STps->eof >= ST_EOD_1) {
- if (STps->eof < ST_EOD) {
- STps->eof += 1;
- return 0;
- }
- return (-EIO); /* EOM or Blank Check */
- }
-
- /* Check the buffer writability before any tape movement. Don't alter
- buffer data. */
- if (copy_from_user(&i, buf, 1) != 0 ||
- copy_to_user(buf, &i, 1) != 0 ||
- copy_from_user(&i, buf + count - 1, 1) != 0 ||
- copy_to_user(buf + count - 1, &i, 1) != 0)
- return (-EFAULT);
-
- STps->rw = ST_READING;
-
-
- /* Loop until enough data in buffer or a special condition found */
- for (total = 0, special = 0; total < count && !special; ) {
-
- /* Get new data if the buffer is empty */
- if ((STp->buffer)->buffer_bytes == 0) {
- special = read_tape(inode, count - total, &SCpnt);
- if (special < 0) { /* No need to continue read */
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- }
- return special;
- }
- }
+ int result = 0, result2;
+ static unsigned char cmd[10];
+ Scsi_Cmnd *SCpnt;
+ Scsi_Tape *STp;
+ ST_mode *STm;
+ ST_partstat *STps;
+
+ struct inode *inode = filp->f_dentry->d_inode;
+ kdev_t devt = inode->i_rdev;
+ int dev;
+
+ if (file_count(filp) > 1)
+ return 0;
+
+ dev = TAPE_NR(devt);
+ STp = &(scsi_tapes[dev]);
+ STm = &(STp->modes[STp->current_mode]);
+ STps = &(STp->ps[STp->partition]);
- /* Move the data from driver buffer to user buffer */
- if ((STp->buffer)->buffer_bytes > 0) {
+ if (STp->can_partitions &&
+ (result = update_partition(inode)) < 0) {
#if DEBUG
- if (debugging && STps->eof != ST_NOEOF)
- printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %d.\n", dev,
- STps->eof, (STp->buffer)->buffer_bytes, count - total);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: update_partition at close failed.\n", dev);
#endif
- transfer = (STp->buffer)->buffer_bytes < count - total ?
- (STp->buffer)->buffer_bytes : count - total;
- i = from_buffer(STp->buffer, buf, transfer);
- if (i) {
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- }
- return i;
+ goto out;
}
- filp->f_pos += transfer;
- buf += transfer;
- total += transfer;
- }
-
- if (STp->block_size == 0)
- break; /* Read only one variable length block */
+ if (STps->rw == ST_WRITING && !(STp->device)->was_reset) {
- } /* for (total = 0, special = 0; total < count && !special; ) */
+ result = flush_write_buffer(STp);
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- }
+#if DEBUG
+ if (debugging) {
+ printk(ST_DEB_MSG "st%d: File length %ld bytes.\n",
+ dev, (long) (filp->f_pos));
+ printk(ST_DEB_MSG "st%d: Async write waits %d, finished %d.\n",
+ dev, STp->nbr_waits, STp->nbr_finished);
+ }
+#endif
- /* Change the eof state if no data from tape or buffer */
- if (total == 0) {
- if (STps->eof == ST_FM_HIT) {
- STps->eof = ST_FM;
- STps->drv_block = 0;
- if (STps->drv_file >= 0)
- STps->drv_file++;
- }
- else if (STps->eof == ST_EOD_1) {
- STps->eof = ST_EOD_2;
- STps->drv_block = 0;
- if (STps->drv_file >= 0)
- STps->drv_file++;
- }
- else if (STps->eof == ST_EOD_2)
- STps->eof = ST_EOD;
- }
- else if (STps->eof == ST_FM)
- STps->eof = ST_NOEOF;
+ if (result == 0 || result == (-ENOSPC)) {
- return total;
+ memset(cmd, 0, 10);
+ cmd[0] = WRITE_FILEMARKS;
+ cmd[4] = 1 + STp->two_fm;
+
+ SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES,
+ TRUE);
+ if (!SCpnt)
+ goto out;
+
+ if ((STp->buffer)->last_result_fatal != 0 &&
+ ((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
+ (SCpnt->sense_buffer[2] & 0x4f) != 0x40 ||
+ ((SCpnt->sense_buffer[0] & 0x80) != 0 &&
+ (SCpnt->sense_buffer[3] | SCpnt->sense_buffer[4] |
+ SCpnt->sense_buffer[5] |
+ SCpnt->sense_buffer[6]) == 0))) {
+ /* Filter out successful write at EOM */
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
+ if (result == 0)
+ result = (-EIO);
+ } else {
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ if (STp->two_fm)
+ cross_eof(STp, FALSE);
+ STps->eof = ST_FM;
+ }
+ }
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Buffer flushed, %d EOF(s) written\n",
+ dev, cmd[4]);
+#endif
+ } else if (!STp->rew_at_close) {
+ STps = &(STp->ps[STp->partition]);
+ if (!STm->sysv || STps->rw != ST_READING) {
+ if (STp->can_bsr)
+ result = flush_buffer(inode, filp, 0);
+ else if (STps->eof == ST_FM_HIT) {
+ result = cross_eof(STp, FALSE);
+ if (result) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ } else
+ STps->eof = ST_NOEOF;
+ }
+ } else if ((STps->eof == ST_NOEOF &&
+ !(result = cross_eof(STp, TRUE))) ||
+ STps->eof == ST_FM_HIT) {
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ }
+ }
+ out:
+ if (STp->rew_at_close) {
+ result2 = st_int_ioctl(inode, MTREW, 1);
+ if (result == 0)
+ result = result2;
+ }
+ return result;
}
-
-/* Set the driver options */
- static void
-st_log_options(Scsi_Tape *STp, ST_mode *STm, int dev)
+/* Close the device and release it */
+static int scsi_tape_close(struct inode *inode, struct file *filp)
{
- printk(KERN_INFO
-"st%d: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
- dev, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
- STm->do_read_ahead);
- printk(KERN_INFO
-"st%d: can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
- dev, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
- printk(KERN_INFO
-"st%d: defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
- dev, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
- STp->scsi2_logical);
- printk(KERN_INFO
-"st%d: sysv: %d\n", dev, STm->sysv);
-#if DEBUG
- printk(KERN_INFO
- "st%d: debugging: %d\n",
- dev, debugging);
-#endif
-}
-
-
- static int
-st_set_options(struct inode * inode, long options)
-{
- int value;
- long code;
- Scsi_Tape *STp;
- ST_mode *STm;
- int dev = TAPE_NR(inode->i_rdev);
-
- STp = &(scsi_tapes[dev]);
- STm = &(STp->modes[STp->current_mode]);
- if (!STm->defined) {
- memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
- modes_defined = TRUE;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Initialized mode %d definition from mode 0\n",
- dev, STp->current_mode);
-#endif
- }
-
- code = options & MT_ST_OPTIONS;
- if (code == MT_ST_BOOLEANS) {
- STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
- STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
- STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
- STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
- STp->two_fm = (options & MT_ST_TWO_FM) != 0;
- STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
- STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0;
- STp->can_bsr = (options & MT_ST_CAN_BSR) != 0;
- STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0;
- if ((STp->device)->scsi_level >= SCSI_2)
- STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
- STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
- STm->sysv = (options & MT_ST_SYSV) != 0;
-#if DEBUG
- debugging = (options & MT_ST_DEBUGGING) != 0;
-#endif
- st_log_options(STp, STm, dev);
- }
- else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
- value = (code == MT_ST_SETBOOLEANS);
- if ((options & MT_ST_BUFFER_WRITES) != 0)
- STm->do_buffer_writes = value;
- if ((options & MT_ST_ASYNC_WRITES) != 0)
- STm->do_async_writes = value;
- if ((options & MT_ST_DEF_WRITES) != 0)
- STm->defaults_for_writes = value;
- if ((options & MT_ST_READ_AHEAD) != 0)
- STm->do_read_ahead = value;
- if ((options & MT_ST_TWO_FM) != 0)
- STp->two_fm = value;
- if ((options & MT_ST_FAST_MTEOM) != 0)
- STp->fast_mteom = value;
- if ((options & MT_ST_AUTO_LOCK) != 0)
- STp->do_auto_lock = value;
- if ((options & MT_ST_CAN_BSR) != 0)
- STp->can_bsr = value;
- if ((options & MT_ST_NO_BLKLIMS) != 0)
- STp->omit_blklims = value;
- if ((STp->device)->scsi_level >= SCSI_2 &&
- (options & MT_ST_CAN_PARTITIONS) != 0)
- STp->can_partitions = value;
- if ((options & MT_ST_SCSI2LOGICAL) != 0)
- STp->scsi2_logical = value;
- if ((options & MT_ST_SYSV) != 0)
- STm->sysv = value;
-#if DEBUG
- if ((options & MT_ST_DEBUGGING) != 0)
- debugging = value;
-#endif
- st_log_options(STp, STm, dev);
- }
- else if (code == MT_ST_WRITE_THRESHOLD) {
- value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE;
- if (value < 1 || value > st_buffer_size) {
- printk(KERN_WARNING "st%d: Write threshold %d too small or too large.\n",
- dev, value);
- return (-EIO);
- }
- STp->write_threshold = value;
- printk(KERN_INFO "st%d: Write threshold set to %d bytes.\n",
- dev, value);
- }
- else if (code == MT_ST_DEF_BLKSIZE) {
- value = (options & ~MT_ST_OPTIONS);
- if (value == ~MT_ST_OPTIONS) {
- STm->default_blksize = (-1);
- printk(KERN_INFO "st%d: Default block size disabled.\n", dev);
- }
- else {
- STm->default_blksize = value;
- printk(KERN_INFO "st%d: Default block size set to %d bytes.\n",
- dev, STm->default_blksize);
- }
- }
- else if (code == MT_ST_TIMEOUTS) {
- value = (options & ~MT_ST_OPTIONS);
- if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
- STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
- printk(KERN_INFO "st%d: Long timeout set to %d seconds.\n", dev,
- (value & ~MT_ST_SET_LONG_TIMEOUT));
- }
- else {
- STp->timeout = value * HZ;
- printk(KERN_INFO "st%d: Normal timeout set to %d seconds.\n", dev,
- value);
- }
- }
- else if (code == MT_ST_DEF_OPTIONS) {
- code = (options & ~MT_ST_CLEAR_DEFAULT);
- value = (options & MT_ST_CLEAR_DEFAULT);
- if (code == MT_ST_DEF_DENSITY) {
- if (value == MT_ST_CLEAR_DEFAULT) {
- STm->default_density = (-1);
- printk(KERN_INFO "st%d: Density default disabled.\n", dev);
- }
- else {
- STm->default_density = value & 0xff;
- printk(KERN_INFO "st%d: Density default set to %x\n",
- dev, STm->default_density);
- }
- }
- else if (code == MT_ST_DEF_DRVBUFFER) {
- if (value == MT_ST_CLEAR_DEFAULT) {
- STp->default_drvbuffer = 0xff;
- printk(KERN_INFO "st%d: Drive buffer default disabled.\n", dev);
- }
- else {
- STp->default_drvbuffer = value & 7;
- printk(KERN_INFO "st%d: Drive buffer default set to %x\n",
- dev, STp->default_drvbuffer);
- }
- }
- else if (code == MT_ST_DEF_COMPRESSION) {
- if (value == MT_ST_CLEAR_DEFAULT) {
- STm->default_compression = ST_DONT_TOUCH;
- printk(KERN_INFO "st%d: Compression default disabled.\n", dev);
- }
- else {
- STm->default_compression = (value & 1 ? ST_YES : ST_NO);
- printk(KERN_INFO "st%d: Compression default set to %x\n",
- dev, (value & 1));
- }
- }
- }
- else
- return (-EIO);
+ int result = 0;
+ Scsi_Tape *STp;
- return 0;
-}
+ kdev_t devt = inode->i_rdev;
+ int dev;
-
-#define COMPRESSION_PAGE 0x0f
-#define COMPRESSION_PAGE_LENGTH 16
+ dev = TAPE_NR(devt);
+ STp = &(scsi_tapes[dev]);
-#define MODE_HEADER_LENGTH 4
+ if (STp->door_locked == ST_LOCKED_AUTO)
+ st_int_ioctl(inode, MTUNLOCK, 0);
-#define DCE_MASK 0x80
-#define DCC_MASK 0x40
-#define RED_MASK 0x60
+ if (STp->buffer != NULL) {
+ normalize_buffer(STp->buffer);
+ (STp->buffer)->in_use = 0;
+ }
+ STp->in_use = 0;
+ if (scsi_tapes[dev].device->host->hostt->module)
+ __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
+ if (st_template.module)
+ __MOD_DEC_USE_COUNT(st_template.module);
+ return result;
+}
+
-/* Control the compression with mode page 15. Algorithm not changed if zero. */
- static int
-st_compression(Scsi_Tape * STp, int state)
+/* Write command */
+static ssize_t
+ st_write(struct file *filp, const char *buf, size_t count, loff_t * ppos)
{
- int dev;
- unsigned char cmd[10];
- Scsi_Cmnd * SCpnt = NULL;
-
- if (STp->ready != ST_READY)
- return (-EIO);
-
- /* Read the current page contents */
- memset(cmd, 0, 10);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8;
- cmd[2] = COMPRESSION_PAGE;
- cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
-
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
- if (SCpnt == NULL)
- return (-EBUSY);
- dev = TAPE_NR(SCpnt->request.rq_dev);
+ struct inode *inode = filp->f_dentry->d_inode;
+ ssize_t total;
+ ssize_t i, do_count, blks, retval, transfer;
+ int write_threshold;
+ int doing_write = 0;
+ static unsigned char cmd[10];
+ const char *b_point;
+ Scsi_Cmnd *SCpnt = NULL;
+ Scsi_Tape *STp;
+ ST_mode *STm;
+ ST_partstat *STps;
+ int dev = TAPE_NR(inode->i_rdev);
+
+ STp = &(scsi_tapes[dev]);
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if (!scsi_block_when_processing_errors(STp->device)) {
+ return -ENXIO;
+ }
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
+ STm = &(STp->modes[STp->current_mode]);
+ if (!STm->defined)
+ return (-ENXIO);
+ if (count == 0)
+ return 0;
+
+ /*
+ * If there was a bus reset, block further access
+ * to this device.
+ */
+ if (STp->device->was_reset)
+ return (-EIO);
+
+#if DEBUG
+ if (!STp->in_use) {
+ printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
+ return (-EIO);
+ }
+#endif
+
+ /* Write must be integral number of blocks */
+ if (STp->block_size != 0 && (count % STp->block_size) != 0) {
+ printk(KERN_WARNING "st%d: Write not multiple of tape block size.\n",
+ dev);
+ return (-EIO);
+ }
+ if (STp->can_partitions &&
+ (retval = update_partition(inode)) < 0)
+ return retval;
+ STps = &(STp->ps[STp->partition]);
- if ((STp->buffer)->last_result_fatal != 0) {
+ if (STp->write_prot)
+ return (-EACCES);
+
+ if (STp->block_size == 0 &&
+ count > (STp->buffer)->buffer_size &&
+ !enlarge_buffer(STp->buffer, count, STp->restr_dma))
+ return (-EOVERFLOW);
+
+ if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
+ !st_int_ioctl(inode, MTLOCK, 0))
+ STp->door_locked = ST_LOCKED_AUTO;
+
+ if (STps->rw == ST_READING) {
+ retval = flush_buffer(inode, filp, 0);
+ if (retval)
+ return retval;
+ STps->rw = ST_WRITING;
+ } else if (STps->rw != ST_WRITING &&
+ STps->drv_file == 0 && STps->drv_block == 0) {
+ if ((retval = set_mode_densblk(inode, STp, STm)) < 0)
+ return retval;
+ if (STm->default_compression != ST_DONT_TOUCH &&
+ !(STp->compression_changed)) {
+ if (st_compression(STp, (STm->default_compression == ST_YES))) {
+ printk(KERN_WARNING "st%d: Can't set default compression.\n",
+ dev);
+ if (modes_defined)
+ return (-EINVAL);
+ }
+ }
+ }
+ if ((STp->buffer)->writing) {
+ write_behind_check(STp);
+ if ((STp->buffer)->last_result_fatal) {
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n", dev,
+ (STp->buffer)->last_result);
#endif
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- return (-EIO);
- }
+ if ((STp->buffer)->last_result == INT_MAX)
+ STps->eof = ST_EOM_OK;
+ else
+ STps->eof = ST_EOM_ERROR;
+ }
+ }
+ if (STps->eof == ST_EOM_OK)
+ return (-ENOSPC);
+ else if (STps->eof == ST_EOM_ERROR)
+ return (-EIO);
+
+ /* Check the buffer readability in cases where copy_user might catch
+ the problems after some tape movement. */
+ if (STp->block_size != 0 &&
+ (copy_from_user(&i, buf, 1) != 0 ||
+ copy_from_user(&i, buf + count - 1, 1) != 0))
+ return (-EFAULT);
+
+ if (!STm->do_buffer_writes) {
+#if 0
+ if (STp->block_size != 0 && (count % STp->block_size) != 0)
+ return (-EIO); /* Write must be integral number of blocks */
+#endif
+ write_threshold = 1;
+ } else
+ write_threshold = (STp->buffer)->buffer_blocks * STp->block_size;
+ if (!STm->do_async_writes)
+ write_threshold--;
+
+ total = count;
+
+ memset(cmd, 0, 10);
+ cmd[0] = WRITE_6;
+ cmd[1] = (STp->block_size != 0);
+
+ STps->rw = ST_WRITING;
+
+ b_point = buf;
+ while ((STp->block_size == 0 && !STm->do_async_writes && count > 0) ||
+ (STp->block_size != 0 &&
+ (STp->buffer)->buffer_bytes + count > write_threshold)) {
+ doing_write = 1;
+ if (STp->block_size == 0)
+ do_count = count;
+ else {
+ do_count = (STp->buffer)->buffer_blocks * STp->block_size -
+ (STp->buffer)->buffer_bytes;
+ if (do_count > count)
+ do_count = count;
+ }
+
+ i = append_to_buffer(b_point, STp->buffer, do_count);
+ if (i) {
+ if (SCpnt != NULL) {
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ }
+ return i;
+ }
+ if (STp->block_size == 0)
+ blks = transfer = do_count;
+ else {
+ blks = (STp->buffer)->buffer_bytes /
+ STp->block_size;
+ transfer = blks * STp->block_size;
+ }
+ cmd[2] = blks >> 16;
+ cmd[3] = blks >> 8;
+ cmd[4] = blks;
+
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout,
+ MAX_WRITE_RETRIES, TRUE);
+ if (!SCpnt)
+ return (-EBUSY);
+
+ if ((STp->buffer)->last_result_fatal != 0) {
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev,
- ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0));
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Error on write:\n", dev);
+#endif
+ if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x40)) {
+ if (STp->block_size != 0 && (SCpnt->sense_buffer[0] & 0x80) != 0)
+ transfer = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+ else if (STp->block_size == 0 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW)
+ transfer = do_count;
+ else
+ transfer = 0;
+ if (STp->block_size != 0)
+ transfer *= STp->block_size;
+ if (transfer <= do_count) {
+ filp->f_pos += do_count - transfer;
+ count -= do_count - transfer;
+ if (STps->drv_block >= 0) {
+ if (STp->block_size == 0 && transfer < do_count)
+ STps->drv_block++;
+ else if (STp->block_size != 0)
+ STps->drv_block += (do_count - transfer) / STp->block_size;
+ }
+ STps->eof = ST_EOM_OK;
+ retval = (-ENOSPC); /* EOM within current request */
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: EOM with %d bytes unwritten.\n",
+ dev, transfer);
+#endif
+ } else {
+ STps->eof = ST_EOM_ERROR;
+ STps->drv_block = (-1); /* Too cautious? */
+ retval = (-EIO); /* EOM for old data */
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: EOM with lost data.\n", dev);
+#endif
+ }
+ } else {
+ STps->drv_block = (-1); /* Too cautious? */
+ retval = (-EIO);
+ }
+
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ (STp->buffer)->buffer_bytes = 0;
+ STp->dirty = 0;
+ if (count < total)
+ return total - count;
+ else
+ return retval;
+ }
+ filp->f_pos += do_count;
+ b_point += do_count;
+ count -= do_count;
+ if (STps->drv_block >= 0) {
+ if (STp->block_size == 0)
+ STps->drv_block++;
+ else
+ STps->drv_block += blks;
+ }
+ (STp->buffer)->buffer_bytes = 0;
+ STp->dirty = 0;
+ }
+ if (count != 0) {
+ STp->dirty = 1;
+ i = append_to_buffer(b_point, STp->buffer, count);
+ if (i) {
+ if (SCpnt != NULL) {
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ }
+ return i;
+ }
+ filp->f_pos += count;
+ count = 0;
+ }
+ if (doing_write && (STp->buffer)->last_result_fatal != 0) {
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ return (STp->buffer)->last_result_fatal;
+ }
+ if (STm->do_async_writes &&
+ (((STp->buffer)->buffer_bytes >= STp->write_threshold &&
+ (STp->buffer)->buffer_bytes >= STp->block_size) ||
+ STp->block_size == 0)) {
+ /* Schedule an asynchronous write */
+ if (STp->block_size == 0)
+ (STp->buffer)->writing = (STp->buffer)->buffer_bytes;
+ else
+ (STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
+ STp->block_size) * STp->block_size;
+ STp->dirty = !((STp->buffer)->writing ==
+ (STp->buffer)->buffer_bytes);
+
+ if (STp->block_size == 0)
+ blks = (STp->buffer)->writing;
+ else
+ blks = (STp->buffer)->writing / STp->block_size;
+ cmd[2] = blks >> 16;
+ cmd[3] = blks >> 8;
+ cmd[4] = blks;
+#if DEBUG
+ STp->write_pending = 1;
#endif
- /* Check if compression can be changed */
- if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, (STp->buffer)->writing, STp->timeout,
+ MAX_WRITE_RETRIES, FALSE);
+ if (SCpnt == NULL)
+ return (-EIO);
+ } else if (SCpnt != NULL) {
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ }
+ STps->at_sm &= (total == 0);
+ if (total > 0)
+ STps->eof = ST_NOEOF;
+
+ return (total);
+}
+
+/* Read data from the tape. Returns zero in the normal case, one if the
+ eof status has changed, and the negative error code in case of a
+ fatal error. Otherwise updates the buffer and the eof state. */
+static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt)
+{
+ int transfer, blks, bytes;
+ static unsigned char cmd[10];
+ Scsi_Cmnd *SCpnt;
+ Scsi_Tape *STp;
+ ST_mode *STm;
+ ST_partstat *STps;
+ int dev = TAPE_NR(inode->i_rdev);
+ int retval = 0;
+
+ if (count == 0)
+ return 0;
+
+ STp = &(scsi_tapes[dev]);
+ STm = &(STp->modes[STp->current_mode]);
+ STps = &(STp->ps[STp->partition]);
+ if (STps->eof == ST_FM_HIT)
+ return 1;
+
+ memset(cmd, 0, 10);
+ cmd[0] = READ_6;
+ cmd[1] = (STp->block_size != 0);
+ if (STp->block_size == 0)
+ blks = bytes = count;
+ else {
+ if (STm->do_read_ahead) {
+ blks = (STp->buffer)->buffer_blocks;
+ bytes = blks * STp->block_size;
+ } else {
+ bytes = count;
+ if (bytes > (STp->buffer)->buffer_size)
+ bytes = (STp->buffer)->buffer_size;
+ blks = bytes / STp->block_size;
+ bytes = blks * STp->block_size;
+ }
+ }
+ cmd[2] = blks >> 16;
+ cmd[3] = blks >> 8;
+ cmd[4] = blks;
+
+ SCpnt = *aSCpnt;
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, STp->timeout, MAX_RETRIES, TRUE);
+ *aSCpnt = SCpnt;
+ if (!SCpnt)
+ return (-EBUSY);
+
+ (STp->buffer)->read_pointer = 0;
+ STps->at_sm = 0;
+
+ /* Something to check */
+ if ((STp->buffer)->last_result_fatal) {
+ retval = 1;
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ dev,
+ SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
+ SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
+ SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
+ SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
+#endif
+ if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
+
+ if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
+ SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
+
+ if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
+ /* Compute the residual count */
+ if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+ transfer = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+ else
+ transfer = 0;
+ if (STp->block_size == 0 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
+ transfer = bytes;
+
+ if (SCpnt->sense_buffer[2] & 0x20) { /* ILI */
+ if (STp->block_size == 0) {
+ if (transfer <= 0)
+ transfer = 0;
+ (STp->buffer)->buffer_bytes = bytes - transfer;
+ } else {
+ scsi_release_command(SCpnt);
+ SCpnt = *aSCpnt = NULL;
+ if (transfer == blks) { /* We did not get anything, error */
+ printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
+ if (STps->drv_block >= 0)
+ STps->drv_block += blks - transfer + 1;
+ st_int_ioctl(inode, MTBSR, 1);
+ return (-EIO);
+ }
+ /* We have some data, deliver it */
+ (STp->buffer)->buffer_bytes = (blks - transfer) *
+ STp->block_size;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG
+ "st%d: ILI but enough data received %ld %d.\n",
+ dev, count, (STp->buffer)->buffer_bytes);
+#endif
+ if (STps->drv_block >= 0)
+ STps->drv_block += 1;
+ if (st_int_ioctl(inode, MTBSR, 1))
+ return (-EIO);
+ }
+ } else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */
+ if (STps->eof != ST_FM_HIT)
+ STps->eof = ST_FM_HIT;
+ else
+ STps->eof = ST_EOD_2;
+ if (STp->block_size == 0)
+ (STp->buffer)->buffer_bytes = 0;
+ else
+ (STp->buffer)->buffer_bytes =
+ bytes - transfer * STp->block_size;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG
+ "st%d: EOF detected (%d bytes read).\n",
+ dev, (STp->buffer)->buffer_bytes);
+#endif
+ } else if (SCpnt->sense_buffer[2] & 0x40) {
+ if (STps->eof == ST_FM)
+ STps->eof = ST_EOD_1;
+ else
+ STps->eof = ST_EOM_OK;
+ if (STp->block_size == 0)
+ (STp->buffer)->buffer_bytes = bytes - transfer;
+ else
+ (STp->buffer)->buffer_bytes =
+ bytes - transfer * STp->block_size;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n",
+ dev, (STp->buffer)->buffer_bytes);
#endif
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- return (-EIO);
- }
+ }
+ }
+ /* end of EOF, EOM, ILI test */
+ else { /* nonzero sense key */
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Tape error while reading.\n", dev);
+#endif
+ STps->drv_block = (-1);
+ if (STps->eof == ST_FM &&
+ (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG
+ "st%d: Zero returned for first BLANK CHECK after EOF.\n",
+ dev);
+#endif
+ STps->eof = ST_EOD_2; /* First BLANK_CHECK after FM */
+ } else /* Some other extended sense code */
+ retval = (-EIO);
+ }
+ }
+ /* End of extended sense test */
+ else { /* Non-extended sense */
+ retval = (STp->buffer)->last_result_fatal;
+ }
- /* Do the change */
- if (state)
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] |= DCE_MASK;
- else
- (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK;
+ }
+ /* End of error handling */
+ else /* Read successful */
+ (STp->buffer)->buffer_bytes = bytes;
+
+ if (STps->drv_block >= 0) {
+ if (STp->block_size == 0)
+ STps->drv_block++;
+ else
+ STps->drv_block += (STp->buffer)->buffer_bytes / STp->block_size;
+ }
+ return retval;
+}
+
- memset(cmd, 0, 10);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
+/* Read command */
+static ssize_t
+ st_read(struct file *filp, char *buf, size_t count, loff_t * ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ ssize_t total;
+ ssize_t i, transfer;
+ int special;
+ Scsi_Cmnd *SCpnt = NULL;
+ Scsi_Tape *STp;
+ ST_mode *STm;
+ ST_partstat *STps;
+ int dev = TAPE_NR(inode->i_rdev);
+
+ STp = &(scsi_tapes[dev]);
+
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if (!scsi_block_when_processing_errors(STp->device)) {
+ return -ENXIO;
+ }
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
+ STm = &(STp->modes[STp->current_mode]);
+ if (!STm->defined)
+ return (-ENXIO);
+#if DEBUG
+ if (!STp->in_use) {
+ printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
+ return (-EIO);
+ }
+#endif
- (STp->buffer)->b_data[0] = 0; /* Reserved data length */
- (STp->buffer)->b_data[1] = 0; /* Reserved media type byte */
- (STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+ if (STp->can_partitions &&
+ (total = update_partition(inode)) < 0)
+ return total;
+
+ if (STp->block_size == 0 &&
+ count > (STp->buffer)->buffer_size &&
+ !enlarge_buffer(STp->buffer, count, STp->restr_dma))
+ return (-EOVERFLOW);
+
+ if (!(STm->do_read_ahead) && STp->block_size != 0 &&
+ (count % STp->block_size) != 0)
+ return (-EIO); /* Read must be integral number of blocks */
+
+ if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
+ !st_int_ioctl(inode, MTLOCK, 0))
+ STp->door_locked = ST_LOCKED_AUTO;
- if ((STp->buffer)->last_result_fatal != 0) {
+ STps = &(STp->ps[STp->partition]);
+ if (STps->rw == ST_WRITING) {
+ transfer = flush_buffer(inode, filp, 0);
+ if (transfer)
+ return transfer;
+ STps->rw = ST_READING;
+ }
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev);
+ if (debugging && STps->eof != ST_NOEOF)
+ printk(ST_DEB_MSG "st%d: EOF/EOM flag up (%d). Bytes %d\n", dev,
+ STps->eof, (STp->buffer)->buffer_bytes);
+#endif
+ if ((STp->buffer)->buffer_bytes == 0 &&
+ STps->eof >= ST_EOD_1) {
+ if (STps->eof < ST_EOD) {
+ STps->eof += 1;
+ return 0;
+ }
+ return (-EIO); /* EOM or Blank Check */
+ }
+ /* Check the buffer writability before any tape movement. Don't alter
+ buffer data. */
+ if (copy_from_user(&i, buf, 1) != 0 ||
+ copy_to_user(buf, &i, 1) != 0 ||
+ copy_from_user(&i, buf + count - 1, 1) != 0 ||
+ copy_to_user(buf + count - 1, &i, 1) != 0)
+ return (-EFAULT);
+
+ STps->rw = ST_READING;
+
+
+ /* Loop until enough data in buffer or a special condition found */
+ for (total = 0, special = 0; total < count && !special;) {
+
+ /* Get new data if the buffer is empty */
+ if ((STp->buffer)->buffer_bytes == 0) {
+ special = read_tape(inode, count - total, &SCpnt);
+ if (special < 0) { /* No need to continue read */
+ if (SCpnt != NULL) {
+ scsi_release_command(SCpnt);
+ }
+ return special;
+ }
+ }
+ /* Move the data from driver buffer to user buffer */
+ if ((STp->buffer)->buffer_bytes > 0) {
+#if DEBUG
+ if (debugging && STps->eof != ST_NOEOF)
+ printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %d.\n", dev,
+ STps->eof, (STp->buffer)->buffer_bytes, count - total);
+#endif
+ transfer = (STp->buffer)->buffer_bytes < count - total ?
+ (STp->buffer)->buffer_bytes : count - total;
+ i = from_buffer(STp->buffer, buf, transfer);
+ if (i) {
+ if (SCpnt != NULL) {
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ }
+ return i;
+ }
+ filp->f_pos += transfer;
+ buf += transfer;
+ total += transfer;
+ }
+ if (STp->block_size == 0)
+ break; /* Read only one variable length block */
+
+ } /* for (total = 0, special = 0; total < count && !special; ) */
+
+ if (SCpnt != NULL) {
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ }
+ /* Change the eof state if no data from tape or buffer */
+ if (total == 0) {
+ if (STps->eof == ST_FM_HIT) {
+ STps->eof = ST_FM;
+ STps->drv_block = 0;
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ } else if (STps->eof == ST_EOD_1) {
+ STps->eof = ST_EOD_2;
+ STps->drv_block = 0;
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ } else if (STps->eof == ST_EOD_2)
+ STps->eof = ST_EOD;
+ } else if (STps->eof == ST_FM)
+ STps->eof = ST_NOEOF;
+
+ return total;
+}
+
+
+
+/* Set the driver options */
+static void st_log_options(Scsi_Tape * STp, ST_mode * STm, int dev)
+{
+ printk(KERN_INFO
+ "st%d: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+ dev, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
+ STm->do_read_ahead);
+ printk(KERN_INFO
+ "st%d: can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
+ dev, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
+ printk(KERN_INFO
+ "st%d: defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
+ dev, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
+ STp->scsi2_logical);
+ printk(KERN_INFO
+ "st%d: sysv: %d\n", dev, STm->sysv);
+#if DEBUG
+ printk(KERN_INFO
+ "st%d: debugging: %d\n",
+ dev, debugging);
#endif
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- return (-EIO);
- }
+}
+
+static int st_set_options(struct inode *inode, long options)
+{
+ int value;
+ long code;
+ Scsi_Tape *STp;
+ ST_mode *STm;
+ int dev = TAPE_NR(inode->i_rdev);
+
+ STp = &(scsi_tapes[dev]);
+ STm = &(STp->modes[STp->current_mode]);
+ if (!STm->defined) {
+ memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
+ modes_defined = TRUE;
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n",
- dev, state);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Initialized mode %d definition from mode 0\n",
+ dev, STp->current_mode);
#endif
+ }
+ code = options & MT_ST_OPTIONS;
+ if (code == MT_ST_BOOLEANS) {
+ STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+ STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
+ STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
+ STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
+ STp->two_fm = (options & MT_ST_TWO_FM) != 0;
+ STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
+ STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0;
+ STp->can_bsr = (options & MT_ST_CAN_BSR) != 0;
+ STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0;
+ if ((STp->device)->scsi_level >= SCSI_2)
+ STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
+ STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
+ STm->sysv = (options & MT_ST_SYSV) != 0;
+#if DEBUG
+ debugging = (options & MT_ST_DEBUGGING) != 0;
+#endif
+ st_log_options(STp, STm, dev);
+ } else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
+ value = (code == MT_ST_SETBOOLEANS);
+ if ((options & MT_ST_BUFFER_WRITES) != 0)
+ STm->do_buffer_writes = value;
+ if ((options & MT_ST_ASYNC_WRITES) != 0)
+ STm->do_async_writes = value;
+ if ((options & MT_ST_DEF_WRITES) != 0)
+ STm->defaults_for_writes = value;
+ if ((options & MT_ST_READ_AHEAD) != 0)
+ STm->do_read_ahead = value;
+ if ((options & MT_ST_TWO_FM) != 0)
+ STp->two_fm = value;
+ if ((options & MT_ST_FAST_MTEOM) != 0)
+ STp->fast_mteom = value;
+ if ((options & MT_ST_AUTO_LOCK) != 0)
+ STp->do_auto_lock = value;
+ if ((options & MT_ST_CAN_BSR) != 0)
+ STp->can_bsr = value;
+ if ((options & MT_ST_NO_BLKLIMS) != 0)
+ STp->omit_blklims = value;
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (options & MT_ST_CAN_PARTITIONS) != 0)
+ STp->can_partitions = value;
+ if ((options & MT_ST_SCSI2LOGICAL) != 0)
+ STp->scsi2_logical = value;
+ if ((options & MT_ST_SYSV) != 0)
+ STm->sysv = value;
+#if DEBUG
+ if ((options & MT_ST_DEBUGGING) != 0)
+ debugging = value;
+#endif
+ st_log_options(STp, STm, dev);
+ } else if (code == MT_ST_WRITE_THRESHOLD) {
+ value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE;
+ if (value < 1 || value > st_buffer_size) {
+ printk(KERN_WARNING "st%d: Write threshold %d too small or too large.\n",
+ dev, value);
+ return (-EIO);
+ }
+ STp->write_threshold = value;
+ printk(KERN_INFO "st%d: Write threshold set to %d bytes.\n",
+ dev, value);
+ } else if (code == MT_ST_DEF_BLKSIZE) {
+ value = (options & ~MT_ST_OPTIONS);
+ if (value == ~MT_ST_OPTIONS) {
+ STm->default_blksize = (-1);
+ printk(KERN_INFO "st%d: Default block size disabled.\n", dev);
+ } else {
+ STm->default_blksize = value;
+ printk(KERN_INFO "st%d: Default block size set to %d bytes.\n",
+ dev, STm->default_blksize);
+ }
+ } else if (code == MT_ST_TIMEOUTS) {
+ value = (options & ~MT_ST_OPTIONS);
+ if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
+ STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
+ printk(KERN_INFO "st%d: Long timeout set to %d seconds.\n", dev,
+ (value & ~MT_ST_SET_LONG_TIMEOUT));
+ } else {
+ STp->timeout = value * HZ;
+ printk(KERN_INFO "st%d: Normal timeout set to %d seconds.\n", dev,
+ value);
+ }
+ } else if (code == MT_ST_DEF_OPTIONS) {
+ code = (options & ~MT_ST_CLEAR_DEFAULT);
+ value = (options & MT_ST_CLEAR_DEFAULT);
+ if (code == MT_ST_DEF_DENSITY) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STm->default_density = (-1);
+ printk(KERN_INFO "st%d: Density default disabled.\n", dev);
+ } else {
+ STm->default_density = value & 0xff;
+ printk(KERN_INFO "st%d: Density default set to %x\n",
+ dev, STm->default_density);
+ }
+ } else if (code == MT_ST_DEF_DRVBUFFER) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STp->default_drvbuffer = 0xff;
+ printk(KERN_INFO "st%d: Drive buffer default disabled.\n", dev);
+ } else {
+ STp->default_drvbuffer = value & 7;
+ printk(KERN_INFO "st%d: Drive buffer default set to %x\n",
+ dev, STp->default_drvbuffer);
+ }
+ } else if (code == MT_ST_DEF_COMPRESSION) {
+ if (value == MT_ST_CLEAR_DEFAULT) {
+ STm->default_compression = ST_DONT_TOUCH;
+ printk(KERN_INFO "st%d: Compression default disabled.\n", dev);
+ } else {
+ STm->default_compression = (value & 1 ? ST_YES : ST_NO);
+ printk(KERN_INFO "st%d: Compression default set to %x\n",
+ dev, (value & 1));
+ }
+ }
+ } else
+ return (-EIO);
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- STp->compression_changed = TRUE;
- return 0;
+ return 0;
}
+
+#define COMPRESSION_PAGE 0x0f
+#define COMPRESSION_PAGE_LENGTH 16
+
+#define MODE_HEADER_LENGTH 4
+
+#define DCE_MASK 0x80
+#define DCC_MASK 0x40
+#define RED_MASK 0x60
+
+
+/* Control the compression with mode page 15. Algorithm not changed if zero. */
+static int st_compression(Scsi_Tape * STp, int state)
+{
+ int dev;
+ unsigned char cmd[10];
+ Scsi_Cmnd *SCpnt = NULL;
+
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ /* Read the current page contents */
+ memset(cmd, 0, 10);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = 8;
+ cmd[2] = COMPRESSION_PAGE;
+ cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+ if (SCpnt == NULL)
+ return (-EBUSY);
+ dev = TAPE_NR(SCpnt->request.rq_dev);
+
+ if ((STp->buffer)->last_result_fatal != 0) {
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n", dev);
+#endif
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ return (-EIO);
+ }
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev,
+ ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0));
+#endif
+
+ /* Check if compression can be changed */
+ if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev);
+#endif
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ return (-EIO);
+ }
+ /* Do the change */
+ if (state)
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] |= DCE_MASK;
+ else
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK;
+
+ memset(cmd, 0, 10);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = 0x10;
+ cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+ (STp->buffer)->b_data[0] = 0; /* Reserved data length */
+ (STp->buffer)->b_data[1] = 0; /* Reserved media type byte */
+ (STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+
+ if ((STp->buffer)->last_result_fatal != 0) {
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev);
+#endif
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ return (-EIO);
+ }
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n",
+ dev, state);
+#endif
+
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ STp->compression_changed = TRUE;
+ return 0;
+}
+
/* Internal ioctl function */
- static int
-st_int_ioctl(struct inode * inode,
- unsigned int cmd_in, unsigned long arg)
-{
- int timeout;
- long ltmp;
- int i, ioctl_result;
- int chg_eof = TRUE;
- unsigned char cmd[10];
- Scsi_Cmnd * SCpnt;
- Scsi_Tape * STp;
- ST_partstat * STps;
- int fileno, blkno, at_sm, undone, datalen;
- int dev = TAPE_NR(inode->i_rdev);
-
- STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY && cmd_in != MTLOAD) {
- if (STp->ready == ST_NO_TAPE)
- return (-ENOMEDIUM);
- else
- return (-EIO);
- }
- timeout = STp->long_timeout;
- STps = &(STp->ps[STp->partition]);
- fileno = STps->drv_file;
- blkno = STps->drv_block;
- at_sm = STps->at_sm;
-
- memset(cmd, 0, 10);
- datalen = 0;
- switch (cmd_in) {
- case MTFSFM:
- chg_eof = FALSE; /* Changed from the FSF after this */
- case MTFSF:
- cmd[0] = SPACE;
- cmd[1] = 0x01; /* Space FileMarks */
- cmd[2] = (arg >> 16);
- cmd[3] = (arg >> 8);
- cmd[4] = arg;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Spacing tape forward over %d filemarks.\n",
- dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
-#endif
- if (fileno >= 0)
- fileno += arg;
- blkno = 0;
- at_sm &= (arg == 0);
- break;
- case MTBSFM:
- chg_eof = FALSE; /* Changed from the FSF after this */
- case MTBSF:
- cmd[0] = SPACE;
- cmd[1] = 0x01; /* Space FileMarks */
- ltmp = (-arg);
- cmd[2] = (ltmp >> 16);
- cmd[3] = (ltmp >> 8);
- cmd[4] = ltmp;
-#if DEBUG
- if (debugging) {
- if (cmd[2] & 0x80)
- ltmp = 0xff000000;
- ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
- printk(ST_DEB_MSG "st%d: Spacing tape backward over %ld filemarks.\n",
- dev, (-ltmp));
- }
-#endif
- if (fileno >= 0)
- fileno -= arg;
- blkno = (-1); /* We can't know the block number */
- at_sm &= (arg == 0);
- break;
- case MTFSR:
- cmd[0] = SPACE;
- cmd[1] = 0x00; /* Space Blocks */
- cmd[2] = (arg >> 16);
- cmd[3] = (arg >> 8);
- cmd[4] = arg;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Spacing tape forward %d blocks.\n", dev,
- cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
-#endif
- if (blkno >= 0)
- blkno += arg;
- at_sm &= (arg == 0);
- break;
- case MTBSR:
- cmd[0] = SPACE;
- cmd[1] = 0x00; /* Space Blocks */
- ltmp = (-arg);
- cmd[2] = (ltmp >> 16);
- cmd[3] = (ltmp >> 8);
- cmd[4] = ltmp;
-#if DEBUG
- if (debugging) {
- if (cmd[2] & 0x80)
- ltmp = 0xff000000;
- ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
- printk(ST_DEB_MSG "st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp));
- }
-#endif
- if (blkno >= 0)
- blkno -= arg;
- at_sm &= (arg == 0);
- break;
- case MTFSS:
- cmd[0] = SPACE;
- cmd[1] = 0x04; /* Space Setmarks */
- cmd[2] = (arg >> 16);
- cmd[3] = (arg >> 8);
- cmd[4] = arg;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Spacing tape forward %d setmarks.\n", dev,
- cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
-#endif
- if (arg != 0) {
- blkno = fileno = (-1);
- at_sm = 1;
- }
- break;
- case MTBSS:
- cmd[0] = SPACE;
- cmd[1] = 0x04; /* Space Setmarks */
- ltmp = (-arg);
- cmd[2] = (ltmp >> 16);
- cmd[3] = (ltmp >> 8);
- cmd[4] = ltmp;
-#if DEBUG
- if (debugging) {
- if (cmd[2] & 0x80)
- ltmp = 0xff000000;
- ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
- printk(ST_DEB_MSG "st%d: Spacing tape backward %ld setmarks.\n",
- dev, (-ltmp));
- }
-#endif
- if (arg != 0) {
- blkno = fileno = (-1);
- at_sm = 1;
- }
- break;
- case MTWEOF:
- case MTWSM:
- if (STp->write_prot)
- return (-EACCES);
- cmd[0] = WRITE_FILEMARKS;
- if (cmd_in == MTWSM)
- cmd[1] = 2;
- cmd[2] = (arg >> 16);
- cmd[3] = (arg >> 8);
- cmd[4] = arg;
- timeout = STp->timeout;
-#if DEBUG
- if (debugging) {
- if (cmd_in == MTWEOF)
- printk(ST_DEB_MSG "st%d: Writing %d filemarks.\n", dev,
- cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
- else
- printk(ST_DEB_MSG "st%d: Writing %d setmarks.\n", dev,
- cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
- }
-#endif
- if (fileno >= 0)
- fileno += arg;
- blkno = 0;
- at_sm = (cmd_in == MTWSM);
- break;
- case MTREW:
- cmd[0] = REZERO_UNIT;
+static int st_int_ioctl(struct inode *inode,
+ unsigned int cmd_in, unsigned long arg)
+{
+ int timeout;
+ long ltmp;
+ int i, ioctl_result;
+ int chg_eof = TRUE;
+ unsigned char cmd[10];
+ Scsi_Cmnd *SCpnt;
+ Scsi_Tape *STp;
+ ST_partstat *STps;
+ int fileno, blkno, at_sm, undone, datalen;
+ int dev = TAPE_NR(inode->i_rdev);
+
+ STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY && cmd_in != MTLOAD) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
+ timeout = STp->long_timeout;
+ STps = &(STp->ps[STp->partition]);
+ fileno = STps->drv_file;
+ blkno = STps->drv_block;
+ at_sm = STps->at_sm;
+
+ memset(cmd, 0, 10);
+ datalen = 0;
+ switch (cmd_in) {
+ case MTFSFM:
+ chg_eof = FALSE; /* Changed from the FSF after this */
+ case MTFSF:
+ cmd[0] = SPACE;
+ cmd[1] = 0x01; /* Space FileMarks */
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Spacing tape forward over %d filemarks.\n",
+ dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+ if (fileno >= 0)
+ fileno += arg;
+ blkno = 0;
+ at_sm &= (arg == 0);
+ break;
+ case MTBSFM:
+ chg_eof = FALSE; /* Changed from the FSF after this */
+ case MTBSF:
+ cmd[0] = SPACE;
+ cmd[1] = 0x01; /* Space FileMarks */
+ ltmp = (-arg);
+ cmd[2] = (ltmp >> 16);
+ cmd[3] = (ltmp >> 8);
+ cmd[4] = ltmp;
+#if DEBUG
+ if (debugging) {
+ if (cmd[2] & 0x80)
+ ltmp = 0xff000000;
+ ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+ printk(ST_DEB_MSG "st%d: Spacing tape backward over %ld filemarks.\n",
+ dev, (-ltmp));
+ }
+#endif
+ if (fileno >= 0)
+ fileno -= arg;
+ blkno = (-1); /* We can't know the block number */
+ at_sm &= (arg == 0);
+ break;
+ case MTFSR:
+ cmd[0] = SPACE;
+ cmd[1] = 0x00; /* Space Blocks */
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Spacing tape forward %d blocks.\n", dev,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+ if (blkno >= 0)
+ blkno += arg;
+ at_sm &= (arg == 0);
+ break;
+ case MTBSR:
+ cmd[0] = SPACE;
+ cmd[1] = 0x00; /* Space Blocks */
+ ltmp = (-arg);
+ cmd[2] = (ltmp >> 16);
+ cmd[3] = (ltmp >> 8);
+ cmd[4] = ltmp;
+#if DEBUG
+ if (debugging) {
+ if (cmd[2] & 0x80)
+ ltmp = 0xff000000;
+ ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+ printk(ST_DEB_MSG "st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp));
+ }
+#endif
+ if (blkno >= 0)
+ blkno -= arg;
+ at_sm &= (arg == 0);
+ break;
+ case MTFSS:
+ cmd[0] = SPACE;
+ cmd[1] = 0x04; /* Space Setmarks */
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Spacing tape forward %d setmarks.\n", dev,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+ if (arg != 0) {
+ blkno = fileno = (-1);
+ at_sm = 1;
+ }
+ break;
+ case MTBSS:
+ cmd[0] = SPACE;
+ cmd[1] = 0x04; /* Space Setmarks */
+ ltmp = (-arg);
+ cmd[2] = (ltmp >> 16);
+ cmd[3] = (ltmp >> 8);
+ cmd[4] = ltmp;
+#if DEBUG
+ if (debugging) {
+ if (cmd[2] & 0x80)
+ ltmp = 0xff000000;
+ ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+ printk(ST_DEB_MSG "st%d: Spacing tape backward %ld setmarks.\n",
+ dev, (-ltmp));
+ }
+#endif
+ if (arg != 0) {
+ blkno = fileno = (-1);
+ at_sm = 1;
+ }
+ break;
+ case MTWEOF:
+ case MTWSM:
+ if (STp->write_prot)
+ return (-EACCES);
+ cmd[0] = WRITE_FILEMARKS;
+ if (cmd_in == MTWSM)
+ cmd[1] = 2;
+ cmd[2] = (arg >> 16);
+ cmd[3] = (arg >> 8);
+ cmd[4] = arg;
+ timeout = STp->timeout;
+#if DEBUG
+ if (debugging) {
+ if (cmd_in == MTWEOF)
+ printk(ST_DEB_MSG "st%d: Writing %d filemarks.\n", dev,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+ else
+ printk(ST_DEB_MSG "st%d: Writing %d setmarks.\n", dev,
+ cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+ }
+#endif
+ if (fileno >= 0)
+ fileno += arg;
+ blkno = 0;
+ at_sm = (cmd_in == MTWSM);
+ break;
+ case MTREW:
+ cmd[0] = REZERO_UNIT;
#if ST_NOWAIT
- cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->timeout;
+ cmd[1] = 1; /* Don't wait for completion */
+ timeout = STp->timeout;
#endif
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Rewinding tape.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Rewinding tape.\n", dev);
#endif
- fileno = blkno = at_sm = 0 ;
- break;
- case MTOFFL:
- case MTLOAD:
- case MTUNLOAD:
- cmd[0] = START_STOP;
- if (cmd_in == MTLOAD)
- cmd[4] |= 1;
- /*
- * If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A
- */
- if (cmd_in != MTOFFL &&
- arg >= 1 + MT_ST_HPLOADER_OFFSET
- && arg <= 6 + MT_ST_HPLOADER_OFFSET) {
-#if DEBUG
- if (debugging) {
- printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n",
- dev, (cmd[4]) ? "" : "un",
- arg - MT_ST_HPLOADER_OFFSET);
- }
+ fileno = blkno = at_sm = 0;
+ break;
+ case MTOFFL:
+ case MTLOAD:
+ case MTUNLOAD:
+ cmd[0] = START_STOP;
+ if (cmd_in == MTLOAD)
+ cmd[4] |= 1;
+ /*
+ * If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A
+ */
+ if (cmd_in != MTOFFL &&
+ arg >= 1 + MT_ST_HPLOADER_OFFSET
+ && arg <= 6 + MT_ST_HPLOADER_OFFSET) {
+#if DEBUG
+ if (debugging) {
+ printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n",
+ dev, (cmd[4]) ? "" : "un",
+ arg - MT_ST_HPLOADER_OFFSET);
+ }
#endif
- cmd[3] = arg - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
- }
+ cmd[3] = arg - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
+ }
#if ST_NOWAIT
- cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->timeout;
+ cmd[1] = 1; /* Don't wait for completion */
+ timeout = STp->timeout;
#else
- timeout = STp->long_timeout;
+ timeout = STp->long_timeout;
#endif
#if DEBUG
- if (debugging) {
- if (cmd_in != MTLOAD)
- printk(ST_DEB_MSG "st%d: Unloading tape.\n", dev);
- else
- printk(ST_DEB_MSG "st%d: Loading tape.\n", dev);
- }
-#endif
- fileno = blkno = at_sm = 0 ;
- break;
- case MTNOP:
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: No op on tape.\n", dev);
-#endif
- return 0; /* Should do something ? */
- break;
- case MTRETEN:
- cmd[0] = START_STOP;
+ if (debugging) {
+ if (cmd_in != MTLOAD)
+ printk(ST_DEB_MSG "st%d: Unloading tape.\n", dev);
+ else
+ printk(ST_DEB_MSG "st%d: Loading tape.\n", dev);
+ }
+#endif
+ fileno = blkno = at_sm = 0;
+ break;
+ case MTNOP:
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: No op on tape.\n", dev);
+#endif
+ return 0; /* Should do something ? */
+ break;
+ case MTRETEN:
+ cmd[0] = START_STOP;
#if ST_NOWAIT
- cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->timeout;
+ cmd[1] = 1; /* Don't wait for completion */
+ timeout = STp->timeout;
#endif
- cmd[4] = 3;
+ cmd[4] = 3;
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Retensioning tape.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Retensioning tape.\n", dev);
#endif
- fileno = blkno = at_sm = 0;
- break;
- case MTEOM:
- if (!STp->fast_mteom) {
- /* space to the end of tape */
- ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
- fileno = STps->drv_file;
- if (STps->eof >= ST_EOD_1)
- return 0;
- /* The next lines would hide the number of spaced FileMarks
- That's why I inserted the previous lines. I had no luck
- with detecting EOM with FSF, so we go now to EOM.
- Joerg Weule */
- }
- else
- fileno = (-1);
- cmd[0] = SPACE;
- cmd[1] = 3;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Spacing to end of recorded medium.\n", dev);
-#endif
- blkno = 0;
- at_sm = 0;
- break;
- case MTERASE:
- if (STp->write_prot)
- return (-EACCES);
- cmd[0] = ERASE;
- cmd[1] = 1; /* To the end of tape */
+ fileno = blkno = at_sm = 0;
+ break;
+ case MTEOM:
+ if (!STp->fast_mteom) {
+ /* space to the end of tape */
+ ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
+ fileno = STps->drv_file;
+ if (STps->eof >= ST_EOD_1)
+ return 0;
+ /* The next lines would hide the number of spaced FileMarks
+ That's why I inserted the previous lines. I had no luck
+ with detecting EOM with FSF, so we go now to EOM.
+ Joerg Weule */
+ } else
+ fileno = (-1);
+ cmd[0] = SPACE;
+ cmd[1] = 3;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Spacing to end of recorded medium.\n", dev);
+#endif
+ blkno = 0;
+ at_sm = 0;
+ break;
+ case MTERASE:
+ if (STp->write_prot)
+ return (-EACCES);
+ cmd[0] = ERASE;
+ cmd[1] = 1; /* To the end of tape */
#if ST_NOWAIT
- cmd[1] |= 2; /* Don't wait for completion */
- timeout = STp->timeout;
+ cmd[1] |= 2; /* Don't wait for completion */
+ timeout = STp->timeout;
#else
- timeout = STp->long_timeout * 8;
+ timeout = STp->long_timeout * 8;
#endif
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Erasing tape.\n", dev);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Erasing tape.\n", dev);
#endif
- fileno = blkno = at_sm = 0 ;
- break;
- case MTLOCK:
- chg_eof = FALSE;
- cmd[0] = ALLOW_MEDIUM_REMOVAL;
- cmd[4] = SCSI_REMOVAL_PREVENT;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Locking drive door.\n", dev);
-#endif;
- break;
- case MTUNLOCK:
- chg_eof = FALSE;
- cmd[0] = ALLOW_MEDIUM_REMOVAL;
- cmd[4] = SCSI_REMOVAL_ALLOW;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Unlocking drive door.\n", dev);
-#endif;
- break;
- case MTSETBLK: /* Set block length */
- case MTSETDENSITY: /* Set tape density */
- case MTSETDRVBUFFER: /* Set drive buffering */
- case SET_DENS_AND_BLK: /* Set density and block size */
- chg_eof = FALSE;
- if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
- return (-EIO); /* Not allowed if data in buffer */
- if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
- (arg & MT_ST_BLKSIZE_MASK) != 0 &&
- ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
- (arg & MT_ST_BLKSIZE_MASK) > STp->max_block ||
- (arg & MT_ST_BLKSIZE_MASK) > st_buffer_size)) {
- printk(KERN_WARNING "st%d: Illegal block size.\n", dev);
- return (-EINVAL);
- }
- cmd[0] = MODE_SELECT;
- cmd[4] = datalen = 12;
-
- memset((STp->buffer)->b_data, 0, 12);
- if (cmd_in == MTSETDRVBUFFER)
- (STp->buffer)->b_data[2] = (arg & 7) << 4;
- else
- (STp->buffer)->b_data[2] =
- STp->drv_buffer << 4;
- (STp->buffer)->b_data[3] = 8; /* block descriptor length */
- if (cmd_in == MTSETDENSITY) {
- (STp->buffer)->b_data[4] = arg;
- STp->density_changed = TRUE; /* At least we tried ;-) */
- }
- else if (cmd_in == SET_DENS_AND_BLK)
- (STp->buffer)->b_data[4] = arg >> 24;
- else
- (STp->buffer)->b_data[4] = STp->density;
- if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
- ltmp = arg & MT_ST_BLKSIZE_MASK;
- if (cmd_in == MTSETBLK)
- STp->blksize_changed = TRUE; /* At least we tried ;-) */
- }
- else
- ltmp = STp->block_size;
- (STp->buffer)->b_data[9] = (ltmp >> 16);
- (STp->buffer)->b_data[10] = (ltmp >> 8);
- (STp->buffer)->b_data[11] = ltmp;
- timeout = STp->timeout;
-#if DEBUG
- if (debugging) {
- if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
- printk(ST_DEB_MSG "st%d: Setting block size to %d bytes.\n", dev,
- (STp->buffer)->b_data[9] * 65536 +
- (STp->buffer)->b_data[10] * 256 +
- (STp->buffer)->b_data[11]);
- if (cmd_in == MTSETDENSITY || cmd_in == SET_DENS_AND_BLK)
- printk(ST_DEB_MSG "st%d: Setting density code to %x.\n", dev,
- (STp->buffer)->b_data[4]);
- if (cmd_in == MTSETDRVBUFFER)
- printk(ST_DEB_MSG "st%d: Setting drive buffer code to %d.\n", dev,
- ((STp->buffer)->b_data[2] >> 4) & 7);
- }
-#endif
- break;
- default:
- return (-ENOSYS);
- }
-
- SCpnt = st_do_scsi(NULL, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE);
- if (!SCpnt)
- return (-EBUSY);
-
- ioctl_result = (STp->buffer)->last_result_fatal;
-
- if (!ioctl_result) { /* SCSI command successful */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- STps->drv_block = blkno;
- STps->drv_file = fileno;
- STps->at_sm = at_sm;
-
- if (cmd_in == MTLOCK)
- STp->door_locked = ST_LOCKED_EXPLICIT;
- else if (cmd_in == MTUNLOCK)
- STp->door_locked = ST_UNLOCKED;
-
- if (cmd_in == MTBSFM)
- ioctl_result = st_int_ioctl(inode, MTFSF, 1);
- else if (cmd_in == MTFSFM)
- ioctl_result = st_int_ioctl(inode, MTBSF, 1);
-
- if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
- STp->block_size = arg & MT_ST_BLKSIZE_MASK;
- if (STp->block_size != 0)
- (STp->buffer)->buffer_blocks =
- (STp->buffer)->buffer_size / STp->block_size;
- (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
- if (cmd_in == SET_DENS_AND_BLK)
- STp->density = arg >> MT_ST_DENSITY_SHIFT;
- }
- else if (cmd_in == MTSETDRVBUFFER)
- STp->drv_buffer = (arg & 7);
- else if (cmd_in == MTSETDENSITY)
- STp->density = arg;
-
- if (cmd_in == MTEOM)
- STps->eof = ST_EOD;
- else if (cmd_in == MTFSF)
- STps->eof = ST_FM;
- else if (chg_eof)
- STps->eof = ST_NOEOF;
-
-
- if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
- STp->rew_at_close = 0;
- else if (cmd_in == MTLOAD) {
- STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STp->ps[i].rw = ST_IDLE;
- STp->ps[i].last_block_valid = FALSE;
- }
- STp->partition = 0;
- }
-
- } else { /* SCSI command was not completely successful. Don't return
- from this block without releasing the SCSI command block! */
-
- if (SCpnt->sense_buffer[2] & 0x40) {
- if (cmd_in != MTBSF && cmd_in != MTBSFM &&
- cmd_in != MTBSR && cmd_in != MTBSS)
- STps->eof = ST_EOM_OK;
- STps->drv_block = 0;
- }
-
- undone = (
- (SCpnt->sense_buffer[3] << 24) +
- (SCpnt->sense_buffer[4] << 16) +
- (SCpnt->sense_buffer[5] << 8) +
- SCpnt->sense_buffer[6] );
- if (cmd_in == MTWEOF &&
- (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x4f) == 0x40 &&
- ((SCpnt->sense_buffer[0] & 0x80) == 0 || undone == 0)) {
- ioctl_result = 0; /* EOF written succesfully at EOM */
- if (fileno >= 0)
- fileno++;
- STps->drv_file = fileno;
- STps->eof = ST_NOEOF;
- }
- else if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
- if (fileno >= 0)
- STps->drv_file = fileno - undone ;
- else
- STps->drv_file = fileno;
- STps->drv_block = 0;
- STps->eof = ST_NOEOF;
- }
- else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
- if (fileno >= 0)
- STps->drv_file = fileno + undone ;
- else
- STps->drv_file = fileno;
- STps->drv_block = 0;
- STps->eof = ST_NOEOF;
- }
- else if (cmd_in == MTFSR) {
- if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
- if (STps->drv_file >= 0)
- STps->drv_file++;
- STps->drv_block = 0;
- STps->eof = ST_FM;
- }
- else {
- if (blkno >= undone)
- STps->drv_block = blkno - undone;
- else
- STps->drv_block = (-1);
- STps->eof = ST_NOEOF;
- }
- }
- else if (cmd_in == MTBSR) {
- if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
- STps->drv_file--;
- STps->drv_block = (-1);
- }
- else {
- if (blkno >= 0)
- STps->drv_block = blkno + undone;
- else
- STps->drv_block = (-1);
- }
- STps->eof = ST_NOEOF;
- }
- else if (cmd_in == MTEOM) {
- STps->drv_file = (-1);
- STps->drv_block = (-1);
- STps->eof = ST_EOD;
- }
- else if (chg_eof)
- STps->eof = ST_NOEOF;
-
- if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
- STps->eof = ST_EOD;
-
- if (cmd_in == MTLOCK)
- STp->door_locked = ST_LOCK_FAILS;
-
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- }
-
- return ioctl_result;
-}
-
-
-/* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
- structure. */
-
- static int
-get_location(struct inode * inode, unsigned int *block, int *partition,
- int logical)
-{
- Scsi_Tape *STp;
- int dev = TAPE_NR(inode->i_rdev);
- int result;
- unsigned char scmd[10];
- Scsi_Cmnd *SCpnt;
-
- STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
-
- memset (scmd, 0, 10);
- if ((STp->device)->scsi_level < SCSI_2) {
- scmd[0] = QFA_REQUEST_BLOCK;
- scmd[4] = 3;
- }
- else {
- scmd[0] = READ_POSITION;
- if (!logical && !STp->scsi2_logical)
- scmd[1] = 1;
- }
- SCpnt = st_do_scsi(NULL, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE);
- if (!SCpnt)
- return (-EBUSY);
-
- if ((STp->buffer)->last_result_fatal != 0 ||
- (STp->device->scsi_level >= SCSI_2 &&
- ((STp->buffer)->b_data[0] & 4) != 0)) {
- *block = *partition = 0;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Can't read tape position.\n", dev);
-#endif
- result = (-EIO);
- }
- else {
- result = 0;
- if ((STp->device)->scsi_level < SCSI_2) {
- *block = ((STp->buffer)->b_data[0] << 16)
- + ((STp->buffer)->b_data[1] << 8)
- + (STp->buffer)->b_data[2];
- *partition = 0;
- }
- else {
- *block = ((STp->buffer)->b_data[4] << 24)
- + ((STp->buffer)->b_data[5] << 16)
- + ((STp->buffer)->b_data[6] << 8)
- + (STp->buffer)->b_data[7];
- *partition = (STp->buffer)->b_data[1];
- if (((STp->buffer)->b_data[0] & 0x80) &&
- (STp->buffer)->b_data[1] == 0) /* BOP of partition 0 */
- STp->ps[0].drv_block = STp->ps[0].drv_file = 0;
- }
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Got tape pos. blk %d part %d.\n", dev,
- *block, *partition);
-#endif
-
- }
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ fileno = blkno = at_sm = 0;
+ break;
+ case MTLOCK:
+ chg_eof = FALSE;
+ cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ cmd[4] = SCSI_REMOVAL_PREVENT;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Locking drive door.\n", dev);
+#endif /* ; */
+ break;
+ case MTUNLOCK:
+ chg_eof = FALSE;
+ cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ cmd[4] = SCSI_REMOVAL_ALLOW;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Unlocking drive door.\n", dev);
+#endif /* ; */
+ break;
+ case MTSETBLK: /* Set block length */
+ case MTSETDENSITY: /* Set tape density */
+ case MTSETDRVBUFFER: /* Set drive buffering */
+ case SET_DENS_AND_BLK: /* Set density and block size */
+ chg_eof = FALSE;
+ if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
+ return (-EIO); /* Not allowed if data in buffer */
+ if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
+ (arg & MT_ST_BLKSIZE_MASK) != 0 &&
+ ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
+ (arg & MT_ST_BLKSIZE_MASK) > STp->max_block ||
+ (arg & MT_ST_BLKSIZE_MASK) > st_buffer_size)) {
+ printk(KERN_WARNING "st%d: Illegal block size.\n", dev);
+ return (-EINVAL);
+ }
+ cmd[0] = MODE_SELECT;
+ cmd[4] = datalen = 12;
+
+ memset((STp->buffer)->b_data, 0, 12);
+ if (cmd_in == MTSETDRVBUFFER)
+ (STp->buffer)->b_data[2] = (arg & 7) << 4;
+ else
+ (STp->buffer)->b_data[2] =
+ STp->drv_buffer << 4;
+ (STp->buffer)->b_data[3] = 8; /* block descriptor length */
+ if (cmd_in == MTSETDENSITY) {
+ (STp->buffer)->b_data[4] = arg;
+ STp->density_changed = TRUE; /* At least we tried ;-) */
+ } else if (cmd_in == SET_DENS_AND_BLK)
+ (STp->buffer)->b_data[4] = arg >> 24;
+ else
+ (STp->buffer)->b_data[4] = STp->density;
+ if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
+ ltmp = arg & MT_ST_BLKSIZE_MASK;
+ if (cmd_in == MTSETBLK)
+ STp->blksize_changed = TRUE; /* At least we tried ;-) */
+ } else
+ ltmp = STp->block_size;
+ (STp->buffer)->b_data[9] = (ltmp >> 16);
+ (STp->buffer)->b_data[10] = (ltmp >> 8);
+ (STp->buffer)->b_data[11] = ltmp;
+ timeout = STp->timeout;
+#if DEBUG
+ if (debugging) {
+ if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
+ printk(ST_DEB_MSG "st%d: Setting block size to %d bytes.\n", dev,
+ (STp->buffer)->b_data[9] * 65536 +
+ (STp->buffer)->b_data[10] * 256 +
+ (STp->buffer)->b_data[11]);
+ if (cmd_in == MTSETDENSITY || cmd_in == SET_DENS_AND_BLK)
+ printk(ST_DEB_MSG "st%d: Setting density code to %x.\n", dev,
+ (STp->buffer)->b_data[4]);
+ if (cmd_in == MTSETDRVBUFFER)
+ printk(ST_DEB_MSG "st%d: Setting drive buffer code to %d.\n", dev,
+ ((STp->buffer)->b_data[2] >> 4) & 7);
+ }
+#endif
+ break;
+ default:
+ return (-ENOSYS);
+ }
+
+ SCpnt = st_do_scsi(NULL, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE);
+ if (!SCpnt)
+ return (-EBUSY);
+
+ ioctl_result = (STp->buffer)->last_result_fatal;
+
+ if (!ioctl_result) { /* SCSI command successful */
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ STps->drv_block = blkno;
+ STps->drv_file = fileno;
+ STps->at_sm = at_sm;
+
+ if (cmd_in == MTLOCK)
+ STp->door_locked = ST_LOCKED_EXPLICIT;
+ else if (cmd_in == MTUNLOCK)
+ STp->door_locked = ST_UNLOCKED;
+
+ if (cmd_in == MTBSFM)
+ ioctl_result = st_int_ioctl(inode, MTFSF, 1);
+ else if (cmd_in == MTFSFM)
+ ioctl_result = st_int_ioctl(inode, MTBSF, 1);
+
+ if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
+ STp->block_size = arg & MT_ST_BLKSIZE_MASK;
+ if (STp->block_size != 0)
+ (STp->buffer)->buffer_blocks =
+ (STp->buffer)->buffer_size / STp->block_size;
+ (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
+ if (cmd_in == SET_DENS_AND_BLK)
+ STp->density = arg >> MT_ST_DENSITY_SHIFT;
+ } else if (cmd_in == MTSETDRVBUFFER)
+ STp->drv_buffer = (arg & 7);
+ else if (cmd_in == MTSETDENSITY)
+ STp->density = arg;
+
+ if (cmd_in == MTEOM)
+ STps->eof = ST_EOD;
+ else if (cmd_in == MTFSF)
+ STps->eof = ST_FM;
+ else if (chg_eof)
+ STps->eof = ST_NOEOF;
+
+
+ if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
+ STp->rew_at_close = 0;
+ else if (cmd_in == MTLOAD) {
+ STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STp->ps[i].rw = ST_IDLE;
+ STp->ps[i].last_block_valid = FALSE;
+ }
+ STp->partition = 0;
+ }
+ } else { /* SCSI command was not completely successful. Don't return
+ from this block without releasing the SCSI command block! */
- return result;
+ if (SCpnt->sense_buffer[2] & 0x40) {
+ if (cmd_in != MTBSF && cmd_in != MTBSFM &&
+ cmd_in != MTBSR && cmd_in != MTBSS)
+ STps->eof = ST_EOM_OK;
+ STps->drv_block = 0;
+ }
+ undone = (
+ (SCpnt->sense_buffer[3] << 24) +
+ (SCpnt->sense_buffer[4] << 16) +
+ (SCpnt->sense_buffer[5] << 8) +
+ SCpnt->sense_buffer[6]);
+ if (cmd_in == MTWEOF &&
+ (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x4f) == 0x40 &&
+ ((SCpnt->sense_buffer[0] & 0x80) == 0 || undone == 0)) {
+ ioctl_result = 0; /* EOF written succesfully at EOM */
+ if (fileno >= 0)
+ fileno++;
+ STps->drv_file = fileno;
+ STps->eof = ST_NOEOF;
+ } else if ((cmd_in == MTFSF) || (cmd_in == MTFSFM)) {
+ if (fileno >= 0)
+ STps->drv_file = fileno - undone;
+ else
+ STps->drv_file = fileno;
+ STps->drv_block = 0;
+ STps->eof = ST_NOEOF;
+ } else if ((cmd_in == MTBSF) || (cmd_in == MTBSFM)) {
+ if (fileno >= 0)
+ STps->drv_file = fileno + undone;
+ else
+ STps->drv_file = fileno;
+ STps->drv_block = 0;
+ STps->eof = ST_NOEOF;
+ } else if (cmd_in == MTFSR) {
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ if (STps->drv_file >= 0)
+ STps->drv_file++;
+ STps->drv_block = 0;
+ STps->eof = ST_FM;
+ } else {
+ if (blkno >= undone)
+ STps->drv_block = blkno - undone;
+ else
+ STps->drv_block = (-1);
+ STps->eof = ST_NOEOF;
+ }
+ } else if (cmd_in == MTBSR) {
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ STps->drv_file--;
+ STps->drv_block = (-1);
+ } else {
+ if (blkno >= 0)
+ STps->drv_block = blkno + undone;
+ else
+ STps->drv_block = (-1);
+ }
+ STps->eof = ST_NOEOF;
+ } else if (cmd_in == MTEOM) {
+ STps->drv_file = (-1);
+ STps->drv_block = (-1);
+ STps->eof = ST_EOD;
+ } else if (chg_eof)
+ STps->eof = ST_NOEOF;
+
+ if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
+ STps->eof = ST_EOD;
+
+ if (cmd_in == MTLOCK)
+ STp->door_locked = ST_LOCK_FAILS;
+
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ }
+
+ return ioctl_result;
+}
+
+
+/* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
+ structure. */
+
+static int get_location(struct inode *inode, unsigned int *block, int *partition,
+ int logical)
+{
+ Scsi_Tape *STp;
+ int dev = TAPE_NR(inode->i_rdev);
+ int result;
+ unsigned char scmd[10];
+ Scsi_Cmnd *SCpnt;
+
+ STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ memset(scmd, 0, 10);
+ if ((STp->device)->scsi_level < SCSI_2) {
+ scmd[0] = QFA_REQUEST_BLOCK;
+ scmd[4] = 3;
+ } else {
+ scmd[0] = READ_POSITION;
+ if (!logical && !STp->scsi2_logical)
+ scmd[1] = 1;
+ }
+ SCpnt = st_do_scsi(NULL, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE);
+ if (!SCpnt)
+ return (-EBUSY);
+
+ if ((STp->buffer)->last_result_fatal != 0 ||
+ (STp->device->scsi_level >= SCSI_2 &&
+ ((STp->buffer)->b_data[0] & 4) != 0)) {
+ *block = *partition = 0;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Can't read tape position.\n", dev);
+#endif
+ result = (-EIO);
+ } else {
+ result = 0;
+ if ((STp->device)->scsi_level < SCSI_2) {
+ *block = ((STp->buffer)->b_data[0] << 16)
+ + ((STp->buffer)->b_data[1] << 8)
+ + (STp->buffer)->b_data[2];
+ *partition = 0;
+ } else {
+ *block = ((STp->buffer)->b_data[4] << 24)
+ + ((STp->buffer)->b_data[5] << 16)
+ + ((STp->buffer)->b_data[6] << 8)
+ + (STp->buffer)->b_data[7];
+ *partition = (STp->buffer)->b_data[1];
+ if (((STp->buffer)->b_data[0] & 0x80) &&
+ (STp->buffer)->b_data[1] == 0) /* BOP of partition 0 */
+ STp->ps[0].drv_block = STp->ps[0].drv_file = 0;
+ }
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Got tape pos. blk %d part %d.\n", dev,
+ *block, *partition);
+#endif
+
+ }
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+
+ return result;
}
/* Set the tape block and partition. Negative partition means that only the
block should be set in vendor specific way. */
- static int
-set_location(struct inode * inode, unsigned int block, int partition,
- int logical)
-{
- Scsi_Tape *STp;
- ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
- int result, p;
- unsigned int blk;
- int timeout;
- unsigned char scmd[10];
- Scsi_Cmnd *SCpnt;
-
- STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
- timeout = STp->long_timeout;
- STps = &(STp->ps[STp->partition]);
-
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Setting block to %d and partition to %d.\n",
- dev, block, partition);
- if (partition < 0)
- return (-EIO);
-#endif
-
- /* Update the location at the partition we are leaving */
- if ((!STp->can_partitions && partition != 0) ||
- partition >= ST_NBR_PARTITIONS)
- return (-EINVAL);
- if (partition != STp->partition) {
- if (get_location(inode, &blk, &p, 1))
- STps->last_block_valid = FALSE;
- else {
- STps->last_block_valid = TRUE;
- STps->last_block_visited = blk;
+static int set_location(struct inode *inode, unsigned int block, int partition,
+ int logical)
+{
+ Scsi_Tape *STp;
+ ST_partstat *STps;
+ int dev = TAPE_NR(inode->i_rdev);
+ int result, p;
+ unsigned int blk;
+ int timeout;
+ unsigned char scmd[10];
+ Scsi_Cmnd *SCpnt;
+
+ STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
+ timeout = STp->long_timeout;
+ STps = &(STp->ps[STp->partition]);
+
#if DEBUG
if (debugging)
- printk(ST_DEB_MSG "st%d: Visited block %d for partition %d saved.\n",
- dev, blk, STp->partition);
+ printk(ST_DEB_MSG "st%d: Setting block to %d and partition to %d.\n",
+ dev, block, partition);
+ if (partition < 0)
+ return (-EIO);
+#endif
+
+ /* Update the location at the partition we are leaving */
+ if ((!STp->can_partitions && partition != 0) ||
+ partition >= ST_NBR_PARTITIONS)
+ return (-EINVAL);
+ if (partition != STp->partition) {
+ if (get_location(inode, &blk, &p, 1))
+ STps->last_block_valid = FALSE;
+ else {
+ STps->last_block_valid = TRUE;
+ STps->last_block_visited = blk;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Visited block %d for partition %d saved.\n",
+ dev, blk, STp->partition);
#endif
- }
- }
-
- memset (scmd, 0, 10);
- if ((STp->device)->scsi_level < SCSI_2) {
- scmd[0] = QFA_SEEK_BLOCK;
- scmd[2] = (block >> 16);
- scmd[3] = (block >> 8);
- scmd[4] = block;
- scmd[5] = 0;
- }
- else {
- scmd[0] = SEEK_10;
- scmd[3] = (block >> 24);
- scmd[4] = (block >> 16);
- scmd[5] = (block >> 8);
- scmd[6] = block;
- if (!logical && !STp->scsi2_logical)
- scmd[1] = 4;
- if (STp->partition != partition) {
- scmd[1] |= 2;
- scmd[8] = partition;
+ }
+ }
+ memset(scmd, 0, 10);
+ if ((STp->device)->scsi_level < SCSI_2) {
+ scmd[0] = QFA_SEEK_BLOCK;
+ scmd[2] = (block >> 16);
+ scmd[3] = (block >> 8);
+ scmd[4] = block;
+ scmd[5] = 0;
+ } else {
+ scmd[0] = SEEK_10;
+ scmd[3] = (block >> 24);
+ scmd[4] = (block >> 16);
+ scmd[5] = (block >> 8);
+ scmd[6] = block;
+ if (!logical && !STp->scsi2_logical)
+ scmd[1] = 4;
+ if (STp->partition != partition) {
+ scmd[1] |= 2;
+ scmd[8] = partition;
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Trying to change partition from %d to %d\n",
- dev, STp->partition, partition);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Trying to change partition from %d to %d\n",
+ dev, STp->partition, partition);
#endif
- }
- }
+ }
+ }
#if ST_NOWAIT
- scmd[1] |= 1; /* Don't wait for completion */
- timeout = STp->timeout;
+ scmd[1] |= 1; /* Don't wait for completion */
+ timeout = STp->timeout;
#endif
- SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES, TRUE);
- if (!SCpnt)
- return (-EBUSY);
-
- STps->drv_block = STps->drv_file = (-1);
- STps->eof = ST_NOEOF;
- if ((STp->buffer)->last_result_fatal != 0) {
- result = (-EIO);
- if (STp->can_partitions &&
- (STp->device)->scsi_level >= SCSI_2 &&
- (p = find_partition(inode)) >= 0)
- STp->partition = p;
- }
- else {
- if (STp->can_partitions) {
- STp->partition = partition;
- STps = &(STp->ps[partition]);
- if (!STps->last_block_valid ||
- STps->last_block_visited != block) {
- STps->at_sm = 0;
- STps->rw = ST_IDLE;
+ SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES, TRUE);
+ if (!SCpnt)
+ return (-EBUSY);
+
+ STps->drv_block = STps->drv_file = (-1);
+ STps->eof = ST_NOEOF;
+ if ((STp->buffer)->last_result_fatal != 0) {
+ result = (-EIO);
+ if (STp->can_partitions &&
+ (STp->device)->scsi_level >= SCSI_2 &&
+ (p = find_partition(inode)) >= 0)
+ STp->partition = p;
+ } else {
+ if (STp->can_partitions) {
+ STp->partition = partition;
+ STps = &(STp->ps[partition]);
+ if (!STps->last_block_valid ||
+ STps->last_block_visited != block) {
+ STps->at_sm = 0;
+ STps->rw = ST_IDLE;
+ }
+ } else
+ STps->at_sm = 0;
+ if (block == 0)
+ STps->drv_block = STps->drv_file = 0;
+ result = 0;
}
- }
- else
- STps->at_sm = 0;
- if (block == 0)
- STps->drv_block = STps->drv_file = 0;
- result = 0;
- }
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
- return result;
+ return result;
}
/* Find the current partition number for the drive status. Called from open and
returns either partition number of negative error code. */
- static int
-find_partition(struct inode *inode)
+static int find_partition(struct inode *inode)
{
- int i, partition;
- unsigned int block;
+ int i, partition;
+ unsigned int block;
- if ((i = get_location(inode, &block, &partition, 1)) < 0)
- return i;
- if (partition >= ST_NBR_PARTITIONS)
- return (-EIO);
- return partition;
+ if ((i = get_location(inode, &block, &partition, 1)) < 0)
+ return i;
+ if (partition >= ST_NBR_PARTITIONS)
+ return (-EIO);
+ return partition;
}
/* Change the partition if necessary */
- static int
-update_partition(struct inode * inode)
+static int update_partition(struct inode *inode)
{
- int dev = TAPE_NR(inode->i_rdev);
- Scsi_Tape *STp;
- ST_partstat *STps;
-
- STp = &(scsi_tapes[dev]);
- if (STp->partition == STp->new_partition)
- return 0;
- STps = &(STp->ps[STp->new_partition]);
- if (!STps->last_block_valid)
- STps->last_block_visited = 0;
- return set_location(inode, STps->last_block_visited, STp->new_partition, 1);
+ int dev = TAPE_NR(inode->i_rdev);
+ Scsi_Tape *STp;
+ ST_partstat *STps;
+
+ STp = &(scsi_tapes[dev]);
+ if (STp->partition == STp->new_partition)
+ return 0;
+ STps = &(STp->ps[STp->new_partition]);
+ if (!STps->last_block_valid)
+ STps->last_block_visited = 0;
+ return set_location(inode, STps->last_block_visited, STp->new_partition, 1);
}
-
-/* Functions for reading and writing the medium partition mode page. These
+
+/* Functions for reading and writing the medium partition mode page. These
seem to work with Wangtek 6200HS and HP C1533A. */
#define PART_PAGE 0x11
@@ -2736,656 +2631,635 @@
/* Get the number of partitions on the tape. As a side effect reads the
mode page into the tape buffer. */
- static int
-nbr_partitions(struct inode * inode)
+static int nbr_partitions(struct inode *inode)
{
- int dev = TAPE_NR(inode->i_rdev), result;
- Scsi_Tape *STp;
- Scsi_Cmnd * SCpnt = NULL;
- unsigned char cmd[10];
-
- STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
-
- memset ((void *) &cmd[0], 0, 10);
- cmd[0] = MODE_SENSE;
- cmd[1] = 8; /* Page format */
- cmd[2] = PART_PAGE;
- cmd[4] = 200;
-
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES, TRUE);
- if (SCpnt == NULL)
- return (-EBUSY);
- scsi_release_command(SCpnt);
- SCpnt = NULL;
-
- if ((STp->buffer)->last_result_fatal != 0) {
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n", dev);
-#endif
- result = (-EIO);
- }
- else {
- result = (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] + 1;
+ int dev = TAPE_NR(inode->i_rdev), result;
+ Scsi_Tape *STp;
+ Scsi_Cmnd *SCpnt = NULL;
+ unsigned char cmd[10];
+
+ STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
+
+ memset((void *) &cmd[0], 0, 10);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = 8; /* Page format */
+ cmd[2] = PART_PAGE;
+ cmd[4] = 200;
+
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES, TRUE);
+ if (SCpnt == NULL)
+ return (-EBUSY);
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+
+ if ((STp->buffer)->last_result_fatal != 0) {
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n", dev);
+#endif
+ result = (-EIO);
+ } else {
+ result = (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] + 1;
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result);
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result);
#endif
- }
+ }
- return result;
+ return result;
}
/* Partition the tape into two partitions if size > 0 or one partition if
size == 0 */
- static int
-partition_tape(struct inode * inode, int size)
+static int partition_tape(struct inode *inode, int size)
{
- int dev = TAPE_NR(inode->i_rdev), result;
- int length;
- Scsi_Tape *STp;
- Scsi_Cmnd * SCpnt = NULL;
- unsigned char cmd[10], *bp;
-
- if ((result = nbr_partitions(inode)) < 0)
- return result;
- STp = &(scsi_tapes[dev]);
-
- /* The mode page is in the buffer. Let's modify it and write it. */
- bp = &((STp->buffer)->b_data[0]);
- if (size <= 0) {
- length = 8;
- bp[MODE_HEADER_LENGTH + 3] = 0;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n", dev);
-#endif
- }
- else {
- length = 10;
- bp[MODE_HEADER_LENGTH + 3] = 1;
- bp[MODE_HEADER_LENGTH + 8] = (size >> 8) & 0xff;
- bp[MODE_HEADER_LENGTH + 9] = size & 0xff;
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st%d: Formatting tape with two partition (1 = %d MB).\n",
- dev, size);
-#endif
- }
- bp[MODE_HEADER_LENGTH + 6] = 0;
- bp[MODE_HEADER_LENGTH + 7] = 0;
- bp[MODE_HEADER_LENGTH + 4] = 0x30; /* IDP | PSUM = MB */
-
- bp[0] = 0;
- bp[1] = 0;
- bp[MODE_HEADER_LENGTH] &= 0x3f;
- bp[MODE_HEADER_LENGTH + 1] = length - 2;
-
- memset(cmd, 0, 10);
- cmd[0] = MODE_SELECT;
- cmd[1] = 0x10;
- cmd[4] = length + MODE_HEADER_LENGTH;
-
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout,
- MAX_READY_RETRIES, TRUE);
- if (SCpnt == NULL)
- return (-EBUSY);
- scsi_release_command(SCpnt);
- SCpnt = NULL;
-
- if ((STp->buffer)->last_result_fatal != 0) {
- printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev);
- result = (-EIO);
- }
- else
- result = 0;
+ int dev = TAPE_NR(inode->i_rdev), result;
+ int length;
+ Scsi_Tape *STp;
+ Scsi_Cmnd *SCpnt = NULL;
+ unsigned char cmd[10], *bp;
+
+ if ((result = nbr_partitions(inode)) < 0)
+ return result;
+ STp = &(scsi_tapes[dev]);
+
+ /* The mode page is in the buffer. Let's modify it and write it. */
+ bp = &((STp->buffer)->b_data[0]);
+ if (size <= 0) {
+ length = 8;
+ bp[MODE_HEADER_LENGTH + 3] = 0;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n", dev);
+#endif
+ } else {
+ length = 10;
+ bp[MODE_HEADER_LENGTH + 3] = 1;
+ bp[MODE_HEADER_LENGTH + 8] = (size >> 8) & 0xff;
+ bp[MODE_HEADER_LENGTH + 9] = size & 0xff;
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG "st%d: Formatting tape with two partition (1 = %d MB).\n",
+ dev, size);
+#endif
+ }
+ bp[MODE_HEADER_LENGTH + 6] = 0;
+ bp[MODE_HEADER_LENGTH + 7] = 0;
+ bp[MODE_HEADER_LENGTH + 4] = 0x30; /* IDP | PSUM = MB */
+
+ bp[0] = 0;
+ bp[1] = 0;
+ bp[MODE_HEADER_LENGTH] &= 0x3f;
+ bp[MODE_HEADER_LENGTH + 1] = length - 2;
- return result;
-}
+ memset(cmd, 0, 10);
+ cmd[0] = MODE_SELECT;
+ cmd[1] = 0x10;
+ cmd[4] = length + MODE_HEADER_LENGTH;
+
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout,
+ MAX_READY_RETRIES, TRUE);
+ if (SCpnt == NULL)
+ return (-EBUSY);
+ scsi_release_command(SCpnt);
+ SCpnt = NULL;
+ if ((STp->buffer)->last_result_fatal != 0) {
+ printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev);
+ result = (-EIO);
+ } else
+ result = 0;
+ return result;
+}
+
+
/* The ioctl command */
- static int
-st_ioctl(struct inode * inode,struct file * file,
- unsigned int cmd_in, unsigned long arg)
-{
- int i, cmd_nr, cmd_type, bt;
- unsigned int blk;
- struct mtop mtc;
- struct mtpos mt_pos;
- Scsi_Tape *STp;
- ST_mode *STm;
- ST_partstat *STps;
- int dev = TAPE_NR(inode->i_rdev);
-
- STp = &(scsi_tapes[dev]);
-#if DEBUG
- if (debugging && !STp->in_use) {
- printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
- return (-EIO);
- }
-#endif
- STm = &(STp->modes[STp->current_mode]);
- STps = &(STp->ps[STp->partition]);
-
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if( !scsi_block_when_processing_errors(STp->device) ) {
- return -ENXIO;
- }
-
- cmd_type = _IOC_TYPE(cmd_in);
- cmd_nr = _IOC_NR(cmd_in);
-
- if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
- if (_IOC_SIZE(cmd_in) != sizeof(mtc))
- return (-EINVAL);
-
- i = copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop));
- if (i)
- return (-EFAULT);
-
- if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
- printk(KERN_WARNING "st%d: MTSETDRVBUFFER only allowed for root.\n", dev);
- return (-EPERM);
- }
- if (!STm->defined &&
- (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0))
- return (-ENXIO);
-
- if (!(STp->device)->was_reset) {
-
- if (STps->eof == ST_FM_HIT) {
- if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) {
- mtc.mt_count -= 1;
- if (STps->drv_file >= 0)
- STps->drv_file += 1;
- }
- else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
- mtc.mt_count += 1;
- if (STps->drv_file >= 0)
- STps->drv_file += 1;
- }
- }
-
- if (mtc.mt_op == MTSEEK) {
- /* Old position must be restored if partition will be changed */
- i = !STp->can_partitions ||
- (STp->new_partition != STp->partition);
- }
- else {
- i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
- mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
- mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
- mtc.mt_op == MTCOMPRESSION;
- }
- i = flush_buffer(inode, file, i);
- if (i < 0)
- return i;
- }
- else {
- /*
- * If there was a bus reset, block further access
- * to this device. If the user wants to rewind the tape,
- * then reset the flag and allow access again.
- */
- if(mtc.mt_op != MTREW &&
- mtc.mt_op != MTOFFL &&
- mtc.mt_op != MTRETEN &&
- mtc.mt_op != MTERASE &&
- mtc.mt_op != MTSEEK &&
- mtc.mt_op != MTEOM)
- return (-EIO);
- STp->device->was_reset = 0;
- if (STp->door_locked != ST_UNLOCKED &&
- STp->door_locked != ST_LOCK_FAILS) {
- if (st_int_ioctl(inode, MTLOCK, 0)) {
- printk(KERN_NOTICE "st%d: Could not relock door after bus reset.\n",
- dev);
- STp->door_locked = ST_UNLOCKED;
- }
- }
- }
-
- if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
- mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
- mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
- STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */
-
- if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
- st_int_ioctl(inode, MTUNLOCK, 0); /* Ignore result! */
-
- if (mtc.mt_op == MTSETDRVBUFFER &&
- (mtc.mt_count & MT_ST_OPTIONS) != 0)
- return st_set_options(inode, mtc.mt_count);
- if (mtc.mt_op == MTSETPART) {
- if (!STp->can_partitions ||
- mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
- return (-EINVAL);
- if (mtc.mt_count >= STp->nbr_partitions &&
- (STp->nbr_partitions = nbr_partitions(inode)) < 0)
- return (-EIO);
- if (mtc.mt_count >= STp->nbr_partitions)
- return (-EINVAL);
- STp->new_partition = mtc.mt_count;
- return 0;
- }
- if (mtc.mt_op == MTMKPART) {
- if (!STp->can_partitions)
- return (-EINVAL);
- if ((i = st_int_ioctl(inode, MTREW, 0)) < 0 ||
- (i = partition_tape(inode, mtc.mt_count)) < 0)
- return i;
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STp->ps[i].rw = ST_IDLE;
- STp->ps[i].at_sm = 0;
- STp->ps[i].last_block_valid = FALSE;
- }
- STp->partition = STp->new_partition = 0;
- STp->nbr_partitions = 1; /* Bad guess ?-) */
- STps->drv_block = STps->drv_file = 0;
- return 0;
- }
- if (mtc.mt_op == MTSEEK) {
- i = set_location(inode, mtc.mt_count, STp->new_partition, 0);
- if (!STp->can_partitions)
- STp->ps[0].rw = ST_IDLE;
- return i;
- }
- if (STp->can_partitions && STp->ready == ST_READY &&
- (i = update_partition(inode)) < 0)
- return i;
- if (mtc.mt_op == MTCOMPRESSION)
- return st_compression(STp, (mtc.mt_count & 1));
- else
- return st_int_ioctl(inode, mtc.mt_op, mtc.mt_count);
- }
-
- if (!STm->defined)
- return (-ENXIO);
-
- if ((i = flush_buffer(inode, file, FALSE)) < 0)
- return i;
- if (STp->can_partitions &&
- (i = update_partition(inode)) < 0)
- return i;
-
- if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
-
- if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
- return (-EINVAL);
-
- (STp->mt_status)->mt_dsreg =
- ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
- ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
- (STp->mt_status)->mt_blkno = STps->drv_block;
- (STp->mt_status)->mt_fileno = STps->drv_file;
- if (STp->block_size != 0) {
- if (STps->rw == ST_WRITING)
- (STp->mt_status)->mt_blkno +=
- (STp->buffer)->buffer_bytes / STp->block_size;
- else if (STps->rw == ST_READING)
- (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes +
- STp->block_size - 1) / STp->block_size;
- }
-
- (STp->mt_status)->mt_gstat = 0;
- if (STp->drv_write_prot)
- (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
- if ((STp->mt_status)->mt_blkno == 0) {
- if ((STp->mt_status)->mt_fileno == 0)
- (STp->mt_status)->mt_gstat |= GMT_BOT(0xffffffff);
- else
- (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
- }
- (STp->mt_status)->mt_resid = STp->partition;
- if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
- (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
- else if (STps->eof >= ST_EOM_OK)
- (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
- if (STp->density == 1)
- (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
- else if (STp->density == 2)
- (STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
- else if (STp->density == 3)
- (STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
- if (STp->ready == ST_READY)
- (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
- if (STp->ready == ST_NO_TAPE)
- (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
- if (STps->at_sm)
- (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
- if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) ||
- STp->drv_buffer != 0)
- (STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff);
-
- i = copy_to_user((char *)arg, (char *)(STp->mt_status),
- sizeof(struct mtget));
- if (i)
- return (-EFAULT);
-
- (STp->mt_status)->mt_erreg = 0; /* Clear after read */
- return 0;
- } /* End of MTIOCGET */
-
- if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
- if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos))
- return (-EINVAL);
- if ((i = get_location(inode, &blk, &bt, 0)) < 0)
- return i;
- mt_pos.mt_blkno = blk;
- i = copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
- if (i)
- return (-EFAULT);
- return 0;
- }
+static int st_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd_in, unsigned long arg)
+{
+ int i, cmd_nr, cmd_type, bt;
+ unsigned int blk;
+ struct mtop mtc;
+ struct mtpos mt_pos;
+ Scsi_Tape *STp;
+ ST_mode *STm;
+ ST_partstat *STps;
+ int dev = TAPE_NR(inode->i_rdev);
+
+ STp = &(scsi_tapes[dev]);
+#if DEBUG
+ if (debugging && !STp->in_use) {
+ printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
+ return (-EIO);
+ }
+#endif
+ STm = &(STp->modes[STp->current_mode]);
+ STps = &(STp->ps[STp->partition]);
- return scsi_ioctl(STp->device, cmd_in, (void *) arg);
-}
+ /*
+ * If we are in the middle of error recovery, don't let anyone
+ * else try and use this device. Also, if error recovery fails, it
+ * may try and take the device offline, in which case all further
+ * access to the device is prohibited.
+ */
+ if (!scsi_block_when_processing_errors(STp->device)) {
+ return -ENXIO;
+ }
+ cmd_type = _IOC_TYPE(cmd_in);
+ cmd_nr = _IOC_NR(cmd_in);
+ if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+ if (_IOC_SIZE(cmd_in) != sizeof(mtc))
+ return (-EINVAL);
+
+ i = copy_from_user((char *) &mtc, (char *) arg, sizeof(struct mtop));
+ if (i)
+ return (-EFAULT);
+
+ if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
+ printk(KERN_WARNING "st%d: MTSETDRVBUFFER only allowed for root.\n", dev);
+ return (-EPERM);
+ }
+ if (!STm->defined &&
+ (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0))
+ return (-ENXIO);
+
+ if (!(STp->device)->was_reset) {
+
+ if (STps->eof == ST_FM_HIT) {
+ if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM || mtc.mt_op == MTEOM) {
+ mtc.mt_count -= 1;
+ if (STps->drv_file >= 0)
+ STps->drv_file += 1;
+ } else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
+ mtc.mt_count += 1;
+ if (STps->drv_file >= 0)
+ STps->drv_file += 1;
+ }
+ }
+ if (mtc.mt_op == MTSEEK) {
+ /* Old position must be restored if partition will be changed */
+ i = !STp->can_partitions ||
+ (STp->new_partition != STp->partition);
+ } else {
+ i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
+ mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
+ mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
+ mtc.mt_op == MTCOMPRESSION;
+ }
+ i = flush_buffer(inode, file, i);
+ if (i < 0)
+ return i;
+ } else {
+ /*
+ * If there was a bus reset, block further access
+ * to this device. If the user wants to rewind the tape,
+ * then reset the flag and allow access again.
+ */
+ if (mtc.mt_op != MTREW &&
+ mtc.mt_op != MTOFFL &&
+ mtc.mt_op != MTRETEN &&
+ mtc.mt_op != MTERASE &&
+ mtc.mt_op != MTSEEK &&
+ mtc.mt_op != MTEOM)
+ return (-EIO);
+ STp->device->was_reset = 0;
+ if (STp->door_locked != ST_UNLOCKED &&
+ STp->door_locked != ST_LOCK_FAILS) {
+ if (st_int_ioctl(inode, MTLOCK, 0)) {
+ printk(KERN_NOTICE "st%d: Could not relock door after bus reset.\n",
+ dev);
+ STp->door_locked = ST_UNLOCKED;
+ }
+ }
+ }
+
+ if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+ mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+ mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
+ STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */
+
+ if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
+ st_int_ioctl(inode, MTUNLOCK, 0); /* Ignore result! */
+
+ if (mtc.mt_op == MTSETDRVBUFFER &&
+ (mtc.mt_count & MT_ST_OPTIONS) != 0)
+ return st_set_options(inode, mtc.mt_count);
+ if (mtc.mt_op == MTSETPART) {
+ if (!STp->can_partitions ||
+ mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
+ return (-EINVAL);
+ if (mtc.mt_count >= STp->nbr_partitions &&
+ (STp->nbr_partitions = nbr_partitions(inode)) < 0)
+ return (-EIO);
+ if (mtc.mt_count >= STp->nbr_partitions)
+ return (-EINVAL);
+ STp->new_partition = mtc.mt_count;
+ return 0;
+ }
+ if (mtc.mt_op == MTMKPART) {
+ if (!STp->can_partitions)
+ return (-EINVAL);
+ if ((i = st_int_ioctl(inode, MTREW, 0)) < 0 ||
+ (i = partition_tape(inode, mtc.mt_count)) < 0)
+ return i;
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STp->ps[i].rw = ST_IDLE;
+ STp->ps[i].at_sm = 0;
+ STp->ps[i].last_block_valid = FALSE;
+ }
+ STp->partition = STp->new_partition = 0;
+ STp->nbr_partitions = 1; /* Bad guess ?-) */
+ STps->drv_block = STps->drv_file = 0;
+ return 0;
+ }
+ if (mtc.mt_op == MTSEEK) {
+ i = set_location(inode, mtc.mt_count, STp->new_partition, 0);
+ if (!STp->can_partitions)
+ STp->ps[0].rw = ST_IDLE;
+ return i;
+ }
+ if (STp->can_partitions && STp->ready == ST_READY &&
+ (i = update_partition(inode)) < 0)
+ return i;
+ if (mtc.mt_op == MTCOMPRESSION)
+ return st_compression(STp, (mtc.mt_count & 1));
+ else
+ return st_int_ioctl(inode, mtc.mt_op, mtc.mt_count);
+ }
+ if (!STm->defined)
+ return (-ENXIO);
+
+ if ((i = flush_buffer(inode, file, FALSE)) < 0)
+ return i;
+ if (STp->can_partitions &&
+ (i = update_partition(inode)) < 0)
+ return i;
+
+ if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+
+ if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
+ return (-EINVAL);
+
+ (STp->mt_status)->mt_dsreg =
+ ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
+ ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
+ (STp->mt_status)->mt_blkno = STps->drv_block;
+ (STp->mt_status)->mt_fileno = STps->drv_file;
+ if (STp->block_size != 0) {
+ if (STps->rw == ST_WRITING)
+ (STp->mt_status)->mt_blkno +=
+ (STp->buffer)->buffer_bytes / STp->block_size;
+ else if (STps->rw == ST_READING)
+ (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes +
+ STp->block_size - 1) / STp->block_size;
+ }
+ (STp->mt_status)->mt_gstat = 0;
+ if (STp->drv_write_prot)
+ (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
+ if ((STp->mt_status)->mt_blkno == 0) {
+ if ((STp->mt_status)->mt_fileno == 0)
+ (STp->mt_status)->mt_gstat |= GMT_BOT(0xffffffff);
+ else
+ (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
+ }
+ (STp->mt_status)->mt_resid = STp->partition;
+ if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
+ (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
+ else if (STps->eof >= ST_EOM_OK)
+ (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
+ if (STp->density == 1)
+ (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
+ else if (STp->density == 2)
+ (STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
+ else if (STp->density == 3)
+ (STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
+ if (STp->ready == ST_READY)
+ (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
+ if (STp->ready == ST_NO_TAPE)
+ (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
+ if (STps->at_sm)
+ (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
+ if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) ||
+ STp->drv_buffer != 0)
+ (STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+
+ i = copy_to_user((char *) arg, (char *) (STp->mt_status),
+ sizeof(struct mtget));
+ if (i)
+ return (-EFAULT);
+
+ (STp->mt_status)->mt_erreg = 0; /* Clear after read */
+ return 0;
+ } /* End of MTIOCGET */
+ if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+ if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos))
+ return (-EINVAL);
+ if ((i = get_location(inode, &blk, &bt, 0)) < 0)
+ return i;
+ mt_pos.mt_blkno = blk;
+ i = copy_to_user((char *) arg, (char *) (&mt_pos), sizeof(struct mtpos));
+ if (i)
+ return (-EFAULT);
+ return 0;
+ }
+ return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+}
+
/* Try to allocate a new tape buffer */
- static ST_buffer *
-new_tape_buffer( int from_initialization, int need_dma )
+static ST_buffer *
+ new_tape_buffer(int from_initialization, int need_dma)
{
- int i, priority, b_size, got = 0, segs = 0;
- ST_buffer *tb;
+ int i, priority, b_size, got = 0, segs = 0;
+ ST_buffer *tb;
- if (st_nbr_buffers >= st_template.dev_max)
- return NULL; /* Should never happen */
+ if (st_nbr_buffers >= st_template.dev_max)
+ return NULL; /* Should never happen */
- if (from_initialization)
- priority = GFP_ATOMIC;
- else
- priority = GFP_KERNEL;
-
- i = sizeof(ST_buffer) + (st_max_sg_segs - 1) * sizeof(struct scatterlist);
- tb = (ST_buffer *)scsi_init_malloc(i, priority);
- if (tb) {
- tb->this_size = i;
- if (need_dma)
- priority |= GFP_DMA;
-
- /* Try to allocate the first segment up to ST_FIRST_ORDER and the
- others big enough to reach the goal */
- for (b_size = PAGE_SIZE << ST_FIRST_ORDER;
- b_size / 2 >= st_buffer_size && b_size > PAGE_SIZE; )
- b_size /= 2;
- for ( ; b_size >= PAGE_SIZE; b_size /= 2) {
- tb->sg[0].address =
- (unsigned char *)scsi_init_malloc(b_size, priority);
- if (tb->sg[0].address != NULL) {
- tb->sg[0].alt_address = NULL;
- tb->sg[0].length = b_size;
- break;
- }
- }
- if (tb->sg[segs].address == NULL) {
- scsi_init_free((char *)tb, tb->this_size);
- tb = NULL;
- }
- else { /* Got something, continue */
-
- for (b_size = PAGE_SIZE;
- st_buffer_size > tb->sg[0].length + (ST_FIRST_SG - 1) * b_size; )
- b_size *= 2;
-
- for (segs=1, got=tb->sg[0].length;
- got < st_buffer_size && segs < ST_FIRST_SG; ) {
- tb->sg[segs].address =
- (unsigned char *)scsi_init_malloc(b_size, priority);
- if (tb->sg[segs].address == NULL) {
- if (st_buffer_size - got <=
- (ST_FIRST_SG - segs) * b_size / 2) {
- b_size /= 2; /* Large enough for the rest of the buffers */
- continue;
- }
- for (i=0; i < segs - 1; i++)
- scsi_init_free(tb->sg[i].address, tb->sg[i].length);
- scsi_init_free((char *)tb, tb->this_size);
- tb = NULL;
- break;
- }
- tb->sg[segs].alt_address = NULL;
- tb->sg[segs].length = b_size;
- got += b_size;
- segs++;
- }
- }
- }
- if (!tb) {
- printk(KERN_NOTICE "st: Can't allocate new tape buffer (nbr %d).\n",
- st_nbr_buffers);
- return NULL;
- }
- tb->sg_segs = tb->orig_sg_segs = segs;
- tb->b_data = tb->sg[0].address;
-
-#if DEBUG
- if (debugging) {
- printk(ST_DEB_MSG
- "st: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n",
- st_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data);
- printk(ST_DEB_MSG
- "st: segment sizes: first %d, last %d bytes.\n",
- tb->sg[0].length, tb->sg[segs-1].length);
- }
-#endif
- tb->in_use = 0;
- tb->dma = need_dma;
- tb->buffer_size = got;
- tb->writing = 0;
- st_buffers[st_nbr_buffers++] = tb;
+ if (from_initialization)
+ priority = GFP_ATOMIC;
+ else
+ priority = GFP_KERNEL;
+
+ i = sizeof(ST_buffer) + (st_max_sg_segs - 1) * sizeof(struct scatterlist);
+ tb = (ST_buffer *) scsi_init_malloc(i, priority);
+ if (tb) {
+ tb->this_size = i;
+ if (need_dma)
+ priority |= GFP_DMA;
+
+ /* Try to allocate the first segment up to ST_FIRST_ORDER and the
+ others big enough to reach the goal */
+ for (b_size = PAGE_SIZE << ST_FIRST_ORDER;
+ b_size / 2 >= st_buffer_size && b_size > PAGE_SIZE;)
+ b_size /= 2;
+ for (; b_size >= PAGE_SIZE; b_size /= 2) {
+ tb->sg[0].address =
+ (unsigned char *) scsi_init_malloc(b_size, priority);
+ if (tb->sg[0].address != NULL) {
+ tb->sg[0].alt_address = NULL;
+ tb->sg[0].length = b_size;
+ break;
+ }
+ }
+ if (tb->sg[segs].address == NULL) {
+ scsi_init_free((char *) tb, tb->this_size);
+ tb = NULL;
+ } else { /* Got something, continue */
+
+ for (b_size = PAGE_SIZE;
+ st_buffer_size > tb->sg[0].length + (ST_FIRST_SG - 1) * b_size;)
+ b_size *= 2;
+
+ for (segs = 1, got = tb->sg[0].length;
+ got < st_buffer_size && segs < ST_FIRST_SG;) {
+ tb->sg[segs].address =
+ (unsigned char *) scsi_init_malloc(b_size, priority);
+ if (tb->sg[segs].address == NULL) {
+ if (st_buffer_size - got <=
+ (ST_FIRST_SG - segs) * b_size / 2) {
+ b_size /= 2; /* Large enough for the rest of the buffers */
+ continue;
+ }
+ for (i = 0; i < segs - 1; i++)
+ scsi_init_free(tb->sg[i].address, tb->sg[i].length);
+ scsi_init_free((char *) tb, tb->this_size);
+ tb = NULL;
+ break;
+ }
+ tb->sg[segs].alt_address = NULL;
+ tb->sg[segs].length = b_size;
+ got += b_size;
+ segs++;
+ }
+ }
+ }
+ if (!tb) {
+ printk(KERN_NOTICE "st: Can't allocate new tape buffer (nbr %d).\n",
+ st_nbr_buffers);
+ return NULL;
+ }
+ tb->sg_segs = tb->orig_sg_segs = segs;
+ tb->b_data = tb->sg[0].address;
+
+#if DEBUG
+ if (debugging) {
+ printk(ST_DEB_MSG
+ "st: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n",
+ st_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data);
+ printk(ST_DEB_MSG
+ "st: segment sizes: first %d, last %d bytes.\n",
+ tb->sg[0].length, tb->sg[segs - 1].length);
+ }
+#endif
+ tb->in_use = 0;
+ tb->dma = need_dma;
+ tb->buffer_size = got;
+ tb->writing = 0;
+ st_buffers[st_nbr_buffers++] = tb;
- return tb;
+ return tb;
}
/* Try to allocate a temporary enlarged tape buffer */
- static int
-enlarge_buffer(ST_buffer *STbuffer, int new_size, int need_dma)
+static int enlarge_buffer(ST_buffer * STbuffer, int new_size, int need_dma)
{
- int segs, nbr, max_segs, b_size, priority, got;
+ int segs, nbr, max_segs, b_size, priority, got;
- normalize_buffer(STbuffer);
+ normalize_buffer(STbuffer);
- max_segs = STbuffer->use_sg;
- if (max_segs > st_max_sg_segs)
- max_segs = st_max_sg_segs;
- nbr = max_segs - STbuffer->sg_segs;
- if (nbr <= 0)
- return FALSE;
-
- priority = GFP_KERNEL;
- if (need_dma)
- priority |= GFP_DMA;
- for (b_size = PAGE_SIZE; b_size * nbr < new_size - STbuffer->buffer_size; )
- b_size *= 2;
-
- for (segs=STbuffer->sg_segs, got=STbuffer->buffer_size;
- segs < max_segs && got < new_size; ) {
- STbuffer->sg[segs].address =
- (unsigned char *)scsi_init_malloc(b_size, priority);
- if (STbuffer->sg[segs].address == NULL) {
- if (new_size - got <= (max_segs - segs) * b_size / 2) {
- b_size /= 2; /* Large enough for the rest of the buffers */
- continue;
- }
- printk(KERN_NOTICE "st: failed to enlarge buffer to %d bytes.\n",
- new_size);
- normalize_buffer(STbuffer);
- return FALSE;
- }
- STbuffer->sg[segs].alt_address = NULL;
- STbuffer->sg[segs].length = b_size;
- STbuffer->sg_segs += 1;
- got += b_size;
- STbuffer->buffer_size = got;
- segs++;
- }
-#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG
- "st: Succeeded to enlarge buffer to %d bytes (segs %d->%d, %d).\n",
- got, STbuffer->orig_sg_segs, STbuffer->sg_segs, b_size);
+ max_segs = STbuffer->use_sg;
+ if (max_segs > st_max_sg_segs)
+ max_segs = st_max_sg_segs;
+ nbr = max_segs - STbuffer->sg_segs;
+ if (nbr <= 0)
+ return FALSE;
+
+ priority = GFP_KERNEL;
+ if (need_dma)
+ priority |= GFP_DMA;
+ for (b_size = PAGE_SIZE; b_size * nbr < new_size - STbuffer->buffer_size;)
+ b_size *= 2;
+
+ for (segs = STbuffer->sg_segs, got = STbuffer->buffer_size;
+ segs < max_segs && got < new_size;) {
+ STbuffer->sg[segs].address =
+ (unsigned char *) scsi_init_malloc(b_size, priority);
+ if (STbuffer->sg[segs].address == NULL) {
+ if (new_size - got <= (max_segs - segs) * b_size / 2) {
+ b_size /= 2; /* Large enough for the rest of the buffers */
+ continue;
+ }
+ printk(KERN_NOTICE "st: failed to enlarge buffer to %d bytes.\n",
+ new_size);
+ normalize_buffer(STbuffer);
+ return FALSE;
+ }
+ STbuffer->sg[segs].alt_address = NULL;
+ STbuffer->sg[segs].length = b_size;
+ STbuffer->sg_segs += 1;
+ got += b_size;
+ STbuffer->buffer_size = got;
+ segs++;
+ }
+#if DEBUG
+ if (debugging)
+ printk(ST_DEB_MSG
+ "st: Succeeded to enlarge buffer to %d bytes (segs %d->%d, %d).\n",
+ got, STbuffer->orig_sg_segs, STbuffer->sg_segs, b_size);
#endif
- return TRUE;
+ return TRUE;
}
/* Release the extra buffer */
- static void
-normalize_buffer(ST_buffer *STbuffer)
+static void normalize_buffer(ST_buffer * STbuffer)
{
- int i;
+ int i;
- for (i=STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
- scsi_init_free(STbuffer->sg[i].address, STbuffer->sg[i].length);
- STbuffer->buffer_size -= STbuffer->sg[i].length;
- }
+ for (i = STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
+ scsi_init_free(STbuffer->sg[i].address, STbuffer->sg[i].length);
+ STbuffer->buffer_size -= STbuffer->sg[i].length;
+ }
#if DEBUG
- if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
- printk(ST_DEB_MSG "st: Buffer at %p normalized to %d bytes (segs %d).\n",
- STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs);
+ if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
+ printk(ST_DEB_MSG "st: Buffer at %p normalized to %d bytes (segs %d).\n",
+ STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs);
#endif
- STbuffer->sg_segs = STbuffer->orig_sg_segs;
+ STbuffer->sg_segs = STbuffer->orig_sg_segs;
}
/* Move data from the user buffer to the tape buffer. Returns zero (success) or
negative error code. */
- static int
-append_to_buffer(const char *ubp, ST_buffer *st_bp, int do_count)
+static int append_to_buffer(const char *ubp, ST_buffer * st_bp, int do_count)
{
- int i, cnt, res, offset;
+ int i, cnt, res, offset;
- for (i=0, offset=st_bp->buffer_bytes;
- i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
- offset -= st_bp->sg[i].length;
- if (i == st_bp->sg_segs) { /* Should never happen */
- printk(KERN_WARNING "st: append_to_buffer offset overflow.\n");
- return (-EIO);
- }
- for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
- cnt = st_bp->sg[i].length - offset < do_count ?
- st_bp->sg[i].length - offset : do_count;
- res = copy_from_user(st_bp->sg[i].address + offset, ubp, cnt);
- if (res)
- return (-EFAULT);
- do_count -= cnt;
- st_bp->buffer_bytes += cnt;
- ubp += cnt;
- offset = 0;
- }
- if (do_count) { /* Should never happen */
- printk(KERN_WARNING "st: append_to_buffer overflow (left %d).\n",
- do_count);
- return (-EIO);
- }
- return 0;
+ for (i = 0, offset = st_bp->buffer_bytes;
+ i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+ offset -= st_bp->sg[i].length;
+ if (i == st_bp->sg_segs) { /* Should never happen */
+ printk(KERN_WARNING "st: append_to_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for (; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length - offset < do_count ?
+ st_bp->sg[i].length - offset : do_count;
+ res = copy_from_user(st_bp->sg[i].address + offset, ubp, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) { /* Should never happen */
+ printk(KERN_WARNING "st: append_to_buffer overflow (left %d).\n",
+ do_count);
+ return (-EIO);
+ }
+ return 0;
}
/* Move data from the tape buffer to the user buffer. Returns zero (success) or
negative error code. */
- static int
-from_buffer(ST_buffer *st_bp, char *ubp, int do_count)
+static int from_buffer(ST_buffer * st_bp, char *ubp, int do_count)
{
- int i, cnt, res, offset;
+ int i, cnt, res, offset;
- for (i=0, offset=st_bp->read_pointer;
- i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
- offset -= st_bp->sg[i].length;
- if (i == st_bp->sg_segs) { /* Should never happen */
- printk(KERN_WARNING "st: from_buffer offset overflow.\n");
- return (-EIO);
- }
- for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
- cnt = st_bp->sg[i].length - offset < do_count ?
- st_bp->sg[i].length - offset : do_count;
- res = copy_to_user(ubp, st_bp->sg[i].address + offset, cnt);
- if (res)
- return (-EFAULT);
- do_count -= cnt;
- st_bp->buffer_bytes -= cnt;
- st_bp->read_pointer += cnt;
- ubp += cnt;
- offset = 0;
- }
- if (do_count) { /* Should never happen */
- printk(KERN_WARNING "st: from_buffer overflow (left %d).\n",
- do_count);
- return (-EIO);
- }
- return 0;
+ for (i = 0, offset = st_bp->read_pointer;
+ i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+ offset -= st_bp->sg[i].length;
+ if (i == st_bp->sg_segs) { /* Should never happen */
+ printk(KERN_WARNING "st: from_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for (; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length - offset < do_count ?
+ st_bp->sg[i].length - offset : do_count;
+ res = copy_to_user(ubp, st_bp->sg[i].address + offset, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes -= cnt;
+ st_bp->read_pointer += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) { /* Should never happen */
+ printk(KERN_WARNING "st: from_buffer overflow (left %d).\n",
+ do_count);
+ return (-EIO);
+ }
+ return 0;
}
/* Validate the options from command line or module parameters */
static void validate_options(void)
{
- if (buffer_kbs > 0)
- st_buffer_size = buffer_kbs * ST_KILOBYTE;
- if (write_threshold_kbs > 0)
- st_write_threshold = write_threshold_kbs * ST_KILOBYTE;
- else if (buffer_kbs > 0)
- st_write_threshold = st_buffer_size - 2048;
- if (st_write_threshold > st_buffer_size) {
- st_write_threshold = st_buffer_size;
- printk(KERN_WARNING "st: write_threshold limited to %d bytes.\n",
- st_write_threshold);
- }
- if (max_buffers >= 0)
- st_max_buffers = max_buffers;
- if (max_sg_segs >= ST_FIRST_SG)
- st_max_sg_segs = max_sg_segs;
+ if (buffer_kbs > 0)
+ st_buffer_size = buffer_kbs * ST_KILOBYTE;
+ if (write_threshold_kbs > 0)
+ st_write_threshold = write_threshold_kbs * ST_KILOBYTE;
+ else if (buffer_kbs > 0)
+ st_write_threshold = st_buffer_size - 2048;
+ if (st_write_threshold > st_buffer_size) {
+ st_write_threshold = st_buffer_size;
+ printk(KERN_WARNING "st: write_threshold limited to %d bytes.\n",
+ st_write_threshold);
+ }
+ if (max_buffers >= 0)
+ st_max_buffers = max_buffers;
+ if (max_sg_segs >= ST_FIRST_SG)
+ st_max_sg_segs = max_sg_segs;
}
#ifndef MODULE
/* Set the boot options. Syntax is defined in README.st.
-*/
+ */
static int __init st_setup(char *str)
{
- int i, len, ints[5];
- char *stp;
+ int i, len, ints[5];
+ char *stp;
- stp = get_options(str, ARRAY_SIZE(ints), ints);
+ stp = get_options(str, ARRAY_SIZE(ints), ints);
- if (ints[0] > 0) {
- for (i=0; i < ints[0] && i < ARRAY_SIZE(parms) ; i++)
- *parms[i].val = ints[i + 1];
- }
- else {
- while (stp != NULL) {
- for (i=0; i < ARRAY_SIZE(parms); i++) {
- len = strlen(parms[i].name);
- if (!strncmp(stp, parms[i].name, len) &&
- (*(stp + len) == ':' || *(stp + len) == '=')) {
- *parms[i].val = simple_strtoul(stp + len + 1, NULL, 0);
- break;
- }
- }
- if (i >= sizeof(parms) / sizeof(struct st_dev_parm))
- printk(KERN_WARNING "st: illegal parameter in '%s'\n",
- stp);
- stp = strchr(stp, ',');
- if (stp)
- stp++;
+ if (ints[0] > 0) {
+ for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++)
+ *parms[i].val = ints[i + 1];
+ } else {
+ while (stp != NULL) {
+ for (i = 0; i < ARRAY_SIZE(parms); i++) {
+ len = strlen(parms[i].name);
+ if (!strncmp(stp, parms[i].name, len) &&
+ (*(stp + len) == ':' || *(stp + len) == '=')) {
+ *parms[i].val = simple_strtoul(stp + len + 1, NULL, 0);
+ break;
+ }
+ }
+ if (i >= sizeof(parms) / sizeof(struct st_dev_parm))
+ printk(KERN_WARNING "st: illegal parameter in '%s'\n",
+ stp);
+ stp = strchr(stp, ',');
+ if (stp)
+ stp++;
+ }
}
- }
- validate_options();
+ validate_options();
- return 1;
+ return 1;
}
__setup("st=", st_setup);
@@ -3393,108 +3267,112 @@
#endif
-static struct file_operations st_fops = {
- NULL, /* lseek - default */
- st_read, /* read - general block-dev read */
- st_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- st_ioctl, /* ioctl */
- NULL, /* mmap */
- scsi_tape_open, /* open */
- scsi_tape_flush, /* flush */
- scsi_tape_close, /* release */
- NULL /* fsync */
+static struct file_operations st_fops =
+{
+ NULL, /* lseek - default */
+ st_read, /* read - general block-dev read */
+ st_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ st_ioctl, /* ioctl */
+ NULL, /* mmap */
+ scsi_tape_open, /* open */
+ scsi_tape_flush, /* flush */
+ scsi_tape_close, /* release */
+ NULL /* fsync */
};
-static int st_attach(Scsi_Device * SDp){
- Scsi_Tape * tpnt;
- ST_mode * STm;
- ST_partstat * STps;
- int i;
-
- if (SDp->type != TYPE_TAPE)
- return 1;
-
- if (st_template.nr_dev >= st_template.dev_max) {
- SDp->attached--;
- return 1;
- }
-
- for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++)
- if(!tpnt->device) break;
-
- if(i >= st_template.dev_max) panic ("scsi_devices corrupt (st)");
-
- scsi_tapes[i].device = SDp;
- if (SDp->scsi_level <= 2)
- scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
- else
- scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2;
-
- tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
- tpnt->dirty = 0;
- tpnt->in_use = 0;
- tpnt->drv_buffer = 1; /* Try buffering if no mode sense */
- tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
- tpnt->density = 0;
- tpnt->do_auto_lock = ST_AUTO_LOCK;
- tpnt->can_bsr = ST_IN_FILE_POS;
- tpnt->can_partitions = 0;
- tpnt->two_fm = ST_TWO_FM;
- tpnt->fast_mteom = ST_FAST_MTEOM;
- tpnt->scsi2_logical = ST_SCSI2LOGICAL;
- tpnt->write_threshold = st_write_threshold;
- tpnt->default_drvbuffer = 0xff; /* No forced buffering */
- tpnt->partition = 0;
- tpnt->new_partition = 0;
- tpnt->nbr_partitions = 0;
- tpnt->timeout = ST_TIMEOUT;
- tpnt->long_timeout = ST_LONG_TIMEOUT;
-
- for (i=0; i < ST_NBR_MODES; i++) {
- STm = &(tpnt->modes[i]);
- STm->defined = FALSE;
- STm->sysv = ST_SYSV;
- STm->defaults_for_writes = 0;
- STm->do_async_writes = ST_ASYNC_WRITES;
- STm->do_buffer_writes = ST_BUFFER_WRITES;
- STm->do_read_ahead = ST_READ_AHEAD;
- STm->default_compression = ST_DONT_TOUCH;
- STm->default_blksize = (-1); /* No forced size */
- STm->default_density = (-1); /* No forced density */
- }
-
- for (i=0; i < ST_NBR_PARTITIONS; i++) {
- STps = &(tpnt->ps[i]);
- STps->rw = ST_IDLE;
- STps->eof = ST_NOEOF;
- STps->at_sm = 0;
- STps->last_block_valid = FALSE;
- STps->drv_block = (-1);
- STps->drv_file = (-1);
- }
+static int st_attach(Scsi_Device * SDp)
+{
+ Scsi_Tape *tpnt;
+ ST_mode *STm;
+ ST_partstat *STps;
+ int i;
+
+ if (SDp->type != TYPE_TAPE)
+ return 1;
+
+ if (st_template.nr_dev >= st_template.dev_max) {
+ SDp->attached--;
+ return 1;
+ }
+ for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
+ if (!tpnt->device)
+ break;
+
+ if (i >= st_template.dev_max)
+ panic("scsi_devices corrupt (st)");
+
+ scsi_tapes[i].device = SDp;
+ if (SDp->scsi_level <= 2)
+ scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
+ else
+ scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2;
+
+ tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
+ tpnt->dirty = 0;
+ tpnt->in_use = 0;
+ tpnt->drv_buffer = 1; /* Try buffering if no mode sense */
+ tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
+ tpnt->density = 0;
+ tpnt->do_auto_lock = ST_AUTO_LOCK;
+ tpnt->can_bsr = ST_IN_FILE_POS;
+ tpnt->can_partitions = 0;
+ tpnt->two_fm = ST_TWO_FM;
+ tpnt->fast_mteom = ST_FAST_MTEOM;
+ tpnt->scsi2_logical = ST_SCSI2LOGICAL;
+ tpnt->write_threshold = st_write_threshold;
+ tpnt->default_drvbuffer = 0xff; /* No forced buffering */
+ tpnt->partition = 0;
+ tpnt->new_partition = 0;
+ tpnt->nbr_partitions = 0;
+ tpnt->timeout = ST_TIMEOUT;
+ tpnt->long_timeout = ST_LONG_TIMEOUT;
+
+ for (i = 0; i < ST_NBR_MODES; i++) {
+ STm = &(tpnt->modes[i]);
+ STm->defined = FALSE;
+ STm->sysv = ST_SYSV;
+ STm->defaults_for_writes = 0;
+ STm->do_async_writes = ST_ASYNC_WRITES;
+ STm->do_buffer_writes = ST_BUFFER_WRITES;
+ STm->do_read_ahead = ST_READ_AHEAD;
+ STm->default_compression = ST_DONT_TOUCH;
+ STm->default_blksize = (-1); /* No forced size */
+ STm->default_density = (-1); /* No forced density */
+ }
- tpnt->current_mode = 0;
- tpnt->modes[0].defined = TRUE;
+ for (i = 0; i < ST_NBR_PARTITIONS; i++) {
+ STps = &(tpnt->ps[i]);
+ STps->rw = ST_IDLE;
+ STps->eof = ST_NOEOF;
+ STps->at_sm = 0;
+ STps->last_block_valid = FALSE;
+ STps->drv_block = (-1);
+ STps->drv_file = (-1);
+ }
- tpnt->density_changed = tpnt->compression_changed =
- tpnt->blksize_changed = FALSE;
+ tpnt->current_mode = 0;
+ tpnt->modes[0].defined = TRUE;
- st_template.nr_dev++;
- return 0;
+ tpnt->density_changed = tpnt->compression_changed =
+ tpnt->blksize_changed = FALSE;
+
+ st_template.nr_dev++;
+ return 0;
};
static int st_detect(Scsi_Device * SDp)
{
- if(SDp->type != TYPE_TAPE) return 0;
+ if (SDp->type != TYPE_TAPE)
+ return 0;
- printk(KERN_WARNING
- "Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n",
- st_template.dev_noticed++,
- SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk(KERN_WARNING
+ "Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n",
+ st_template.dev_noticed++,
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
- return 1;
+ return 1;
}
static int st_registered = 0;
@@ -3502,142 +3380,142 @@
/* Driver initialization (not __init because may be called later) */
static int st_init()
{
- int i;
- Scsi_Tape * STp;
- int target_nbr;
-
- if (st_template.dev_noticed == 0) return 0;
-
- printk(KERN_INFO "st: bufsize %d, wrt %d, max init. buffers %d, s/g segs %d.\n",
- st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs);
-
- if(!st_registered) {
- if (register_chrdev(SCSI_TAPE_MAJOR,"st",&st_fops)) {
- printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",MAJOR_NR);
- return 1;
- }
- st_registered++;
- }
-
- if (scsi_tapes) return 0;
- st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
- if (st_template.dev_max < ST_MAX_TAPES)
- st_template.dev_max = ST_MAX_TAPES;
- if (st_template.dev_max > 128 / ST_NBR_MODES)
- printk(KERN_INFO "st: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
- scsi_tapes =
- (Scsi_Tape *) scsi_init_malloc(st_template.dev_max * sizeof(Scsi_Tape),
- GFP_ATOMIC);
- if (scsi_tapes == NULL) {
- printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n");
- unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- return 1;
- }
-
-#if DEBUG
- printk(ST_DEB_MSG "st: Buffer size %d bytes, write threshold %d bytes.\n",
- st_buffer_size, st_write_threshold);
-#endif
-
- memset(scsi_tapes, 0, st_template.dev_max * sizeof(Scsi_Tape));
- for (i=0; i < st_template.dev_max; ++i) {
- STp = &(scsi_tapes[i]);
- STp->capacity = 0xfffff;
- STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget),
- GFP_ATOMIC);
- /* Initialize status */
- memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
- }
-
- /* Allocate the buffers */
- st_buffers =
- (ST_buffer **) scsi_init_malloc(st_template.dev_max * sizeof(ST_buffer *),
- GFP_ATOMIC);
- if (st_buffers == NULL) {
- printk(KERN_ERR "Unable to allocate tape buffer pointers.\n");
- unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- scsi_init_free((char *) scsi_tapes,
- st_template.dev_max * sizeof(Scsi_Tape));
- return 1;
- }
-
- target_nbr = st_template.dev_noticed;
- if (target_nbr < ST_EXTRA_DEVS)
- target_nbr = ST_EXTRA_DEVS;
- if (target_nbr > st_max_buffers)
- target_nbr = st_max_buffers;
-
- for (i=st_nbr_buffers=0; i < target_nbr; i++) {
- if (!new_tape_buffer(TRUE, TRUE)) {
- if (i == 0) {
- printk(KERN_INFO "No tape buffers allocated at initialization.\n");
- break;
- }
- printk(KERN_INFO "Number of tape buffers adjusted.\n");
- break;
- }
- }
+ int i;
+ Scsi_Tape *STp;
+ int target_nbr;
+
+ if (st_template.dev_noticed == 0)
+ return 0;
+
+ printk(KERN_INFO "st: bufsize %d, wrt %d, max init. buffers %d, s/g segs %d.\n",
+ st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs);
+
+ if (!st_registered) {
+ if (register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) {
+ printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", MAJOR_NR);
+ return 1;
+ }
+ st_registered++;
+ }
+ if (scsi_tapes)
+ return 0;
+ st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
+ if (st_template.dev_max < ST_MAX_TAPES)
+ st_template.dev_max = ST_MAX_TAPES;
+ if (st_template.dev_max > 128 / ST_NBR_MODES)
+ printk(KERN_INFO "st: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
+ scsi_tapes =
+ (Scsi_Tape *) scsi_init_malloc(st_template.dev_max * sizeof(Scsi_Tape),
+ GFP_ATOMIC);
+ if (scsi_tapes == NULL) {
+ printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n");
+ unregister_chrdev(SCSI_TAPE_MAJOR, "st");
+ return 1;
+ }
+#if DEBUG
+ printk(ST_DEB_MSG "st: Buffer size %d bytes, write threshold %d bytes.\n",
+ st_buffer_size, st_write_threshold);
+#endif
+
+ memset(scsi_tapes, 0, st_template.dev_max * sizeof(Scsi_Tape));
+ for (i = 0; i < st_template.dev_max; ++i) {
+ STp = &(scsi_tapes[i]);
+ STp->capacity = 0xfffff;
+ STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget),
+ GFP_ATOMIC);
+ /* Initialize status */
+ memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
+ }
- return 0;
+ /* Allocate the buffers */
+ st_buffers =
+ (ST_buffer **) scsi_init_malloc(st_template.dev_max * sizeof(ST_buffer *),
+ GFP_ATOMIC);
+ if (st_buffers == NULL) {
+ printk(KERN_ERR "Unable to allocate tape buffer pointers.\n");
+ unregister_chrdev(SCSI_TAPE_MAJOR, "st");
+ scsi_init_free((char *) scsi_tapes,
+ st_template.dev_max * sizeof(Scsi_Tape));
+ return 1;
+ }
+ target_nbr = st_template.dev_noticed;
+ if (target_nbr < ST_EXTRA_DEVS)
+ target_nbr = ST_EXTRA_DEVS;
+ if (target_nbr > st_max_buffers)
+ target_nbr = st_max_buffers;
+
+ for (i = st_nbr_buffers = 0; i < target_nbr; i++) {
+ if (!new_tape_buffer(TRUE, TRUE)) {
+ if (i == 0) {
+ printk(KERN_INFO "No tape buffers allocated at initialization.\n");
+ break;
+ }
+ printk(KERN_INFO "Number of tape buffers adjusted.\n");
+ break;
+ }
+ }
+
+ return 0;
}
static void st_detach(Scsi_Device * SDp)
{
- Scsi_Tape * tpnt;
- int i;
+ Scsi_Tape *tpnt;
+ int i;
- for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++)
- if(tpnt->device == SDp) {
- tpnt->device = NULL;
- SDp->attached--;
- st_template.nr_dev--;
- st_template.dev_noticed--;
- return;
- }
- return;
+ for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++)
+ if (tpnt->device == SDp) {
+ tpnt->device = NULL;
+ SDp->attached--;
+ st_template.nr_dev--;
+ st_template.dev_noticed--;
+ return;
+ }
+ return;
}
#ifdef MODULE
-int __init init_module(void) {
- int result;
+int __init init_module(void)
+{
+ int result;
- validate_options();
+ validate_options();
- st_template.module = &__this_module;
- result = scsi_register_module(MODULE_SCSI_DEV, &st_template);
- if (result)
- return result;
+ st_template.module = &__this_module;
+ result = scsi_register_module(MODULE_SCSI_DEV, &st_template);
+ if (result)
+ return result;
- return 0;
+ return 0;
}
-void cleanup_module( void)
+void cleanup_module(void)
{
- int i, j;
+ int i, j;
- scsi_unregister_module(MODULE_SCSI_DEV, &st_template);
- unregister_chrdev(SCSI_TAPE_MAJOR, "st");
- st_registered--;
- if(scsi_tapes != NULL) {
- scsi_init_free((char *) scsi_tapes,
- st_template.dev_max * sizeof(Scsi_Tape));
-
- if (st_buffers != NULL) {
- for (i=0; i < st_nbr_buffers; i++)
- if (st_buffers[i] != NULL) {
- for (j=0; j < st_buffers[i]->sg_segs; j++)
- scsi_init_free((char *) st_buffers[i]->sg[j].address,
- st_buffers[i]->sg[j].length);
- scsi_init_free((char *) st_buffers[i], st_buffers[i]->this_size);
+ scsi_unregister_module(MODULE_SCSI_DEV, &st_template);
+ unregister_chrdev(SCSI_TAPE_MAJOR, "st");
+ st_registered--;
+ if (scsi_tapes != NULL) {
+ scsi_init_free((char *) scsi_tapes,
+ st_template.dev_max * sizeof(Scsi_Tape));
+
+ if (st_buffers != NULL) {
+ for (i = 0; i < st_nbr_buffers; i++)
+ {
+ if (st_buffers[i] != NULL) {
+ for (j = 0; j < st_buffers[i]->sg_segs; j++)
+ scsi_init_free((char *) st_buffers[i]->sg[j].address,
+ st_buffers[i]->sg[j].length);
+ scsi_init_free((char *) st_buffers[i], st_buffers[i]->this_size);
+ }
+ }
+ scsi_init_free((char *) st_buffers, st_template.dev_max * sizeof(ST_buffer *));
+ }
}
-
- scsi_init_free((char *) st_buffers,
- st_template.dev_max * sizeof(ST_buffer *));
- }
- }
- st_template.dev_max = 0;
- printk(KERN_INFO "st: Unloaded.\n");
+ st_template.dev_max = 0;
+ printk(KERN_INFO "st: Unloaded.\n");
}
-#endif /* MODULE */
+#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)