summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/st.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/st.c')
-rw-r--r--drivers/scsi/st.c455
1 files changed, 331 insertions, 124 deletions
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index ea78d19c6..384fb6462 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -5,18 +5,19 @@
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, J"org Weule, and
- Eric Youngdale.
+ order) Klaus Ehrenfried, Steve Hirsch, Wolfgang Denk, Andreas Koppenh"ofer,
+ J"org Weule, and Eric Youngdale.
- Copyright 1992, 1993, 1994 Kai Makisara
- email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
+ Copyright 1992, 1993, 1994, 1995 Kai Makisara
+ email Kai.Makisara@metla.fi
- Last modified: Tue Oct 25 19:37:33 1994 by root@kai.home
+ Last modified: Sat Feb 18 10:51:25 1995 by root@kai.home
*/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/mtio.h>
@@ -43,6 +44,8 @@
#define ST_TWO_FM 0
+#define ST_FAST_MTEOM 0
+
#define ST_BUFFER_WRITES 1
#define ST_ASYNC_WRITES 1
@@ -51,7 +54,7 @@
#define ST_BLOCK_SIZE 1024
-#define ST_MAX_BUFFERS 2
+#define ST_MAX_BUFFERS (2 + ST_EXTRA_DEVS)
#define ST_BUFFER_BLOCKS 32
@@ -71,10 +74,11 @@ static int debugging = 1;
#endif
#define MAX_RETRIES 0
+#define MAX_WRITE_RETRIES 0
#define MAX_READY_RETRIES 5
#define NO_TAPE NOT_READY
-#define ST_TIMEOUT 27000
+#define ST_TIMEOUT 90000
#define ST_LONG_TIMEOUT 200000
static int st_nbr_buffers;
@@ -86,13 +90,14 @@ static int st_max_buffers = ST_MAX_BUFFERS;
static Scsi_Tape * scsi_tapes;
static void st_init(void);
-static void st_attach(Scsi_Device *);
+static int st_attach(Scsi_Device *);
static int st_detect(Scsi_Device *);
+static void st_detach(Scsi_Device *);
struct Scsi_Device_Template st_template = {NULL, "tape", "st", TYPE_TAPE,
SCSI_TAPE_MAJOR, 0, 0, 0, 0,
st_detect, st_init,
- NULL, st_attach, NULL};
+ NULL, st_attach, st_detach};
static int st_int_ioctl(struct inode * inode,struct file * file,
unsigned int cmd_in, unsigned long arg);
@@ -106,36 +111,48 @@ st_chk_result(Scsi_Cmnd * SCpnt)
{
int dev = SCpnt->request.dev;
int result = SCpnt->result;
- unsigned char * sense = SCpnt->sense_buffer;
+ unsigned char * sense = SCpnt->sense_buffer, scode;
char *stp;
- if (!result && SCpnt->sense_buffer[0] == 0)
+ if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
return 0;
#ifdef DEBUG
if (debugging) {
printk("st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result,
- SCpnt->cmnd[0], SCpnt->cmnd[1], SCpnt->cmnd[2],
- SCpnt->cmnd[3], SCpnt->cmnd[4], SCpnt->cmnd[5],
+ 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);
}
#endif
-/* if ((sense[0] & 0x70) == 0x70 &&
- ((sense[2] & 0x80) ))
- return 0; */
+ scode = sense[2] & 0x0f;
+ 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)) { /* Abnormal conditions for tape */
+ printk("st%d: Error %x. ", dev, result);
+ if (driver_byte(result) & DRIVER_SENSE)
+ print_sense("st", SCpnt);
+ else
+ printk("\n");
+ }
+
if ((sense[0] & 0x70) == 0x70 &&
- sense[2] == RECOVERED_ERROR
+ scode == RECOVERED_ERROR
#ifdef ST_RECOVERED_WRITE_FATAL
- && SCpnt->cmnd[0] != WRITE_6
- && SCpnt->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 (SCpnt->cmnd[0] == READ_6)
+ if (SCpnt->data_cmnd[0] == READ_6)
stp = "read";
- else if (SCpnt->cmnd[0] == WRITE_6)
+ else if (SCpnt->data_cmnd[0] == WRITE_6)
stp = "write";
else
stp = "ioctl";
@@ -195,17 +212,19 @@ write_behind_check(int dev)
{
Scsi_Tape * STp;
ST_buffer * STbuffer;
+ unsigned long flags;
STp = &(scsi_tapes[dev]);
STbuffer = STp->buffer;
+ save_flags(flags);
cli();
if (STbuffer->last_result < 0) {
STbuffer->writing = (- STbuffer->writing);
sleep_on( &(STp->waiting) );
STbuffer->writing = (- STbuffer->writing);
}
- sti();
+ restore_flags(flags);
if (STbuffer->writing < STbuffer->buffer_bytes)
memcpy(STbuffer->b_data,
@@ -232,6 +251,7 @@ back_over_eof(int dev)
Scsi_Cmnd *SCpnt;
Scsi_Tape *STp = &(scsi_tapes[dev]);
unsigned char cmd[10];
+ unsigned int flags;
cmd[0] = SPACE;
cmd[1] = 0x01; /* Space FileMarks */
@@ -245,11 +265,17 @@ back_over_eof(int dev)
(void *) cmd, (void *) (STp->buffer)->b_data, 0,
st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ /* need to do the check with interrupts off. -RAB */
+ save_flags(flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
SCpnt->request.dev = -1;
if ((STp->buffer)->last_result != 0) {
printk("st%d: Backing over filemark failed.\n", dev);
- (STp->mt_status)->mt_fileno += 1;
+ if ((STp->mt_status)->mt_fileno >= 0)
+ (STp->mt_status)->mt_fileno += 1;
(STp->mt_status)->mt_blkno = 0;
}
@@ -263,6 +289,7 @@ flush_write_buffer(int dev)
{
int offset, transfer, blks;
int result;
+ unsigned int flags;
unsigned char cmd[10];
Scsi_Cmnd *SCpnt;
Scsi_Tape *STp = &(scsi_tapes[dev]);
@@ -305,10 +332,14 @@ flush_write_buffer(int dev)
SCpnt->request.dev = dev;
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data, transfer,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
-
+ restore_flags(flags);
+
if ((STp->buffer)->last_result_fatal != 0) {
printk("st%d: Error on flush.\n", dev);
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
@@ -348,6 +379,9 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next)
STp = &(scsi_tapes[dev]);
STbuffer = STp->buffer;
+ if (STp->ready != ST_READY)
+ return 0;
+
if (STp->rw == ST_WRITING) /* Writing */
return flush_write_buffer(dev);
@@ -383,6 +417,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
{
int dev;
unsigned short flags;
+ unsigned int processor_flags;
int i;
unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
@@ -415,6 +450,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
STp->dirty = 0;
STp->rw = ST_IDLE;
+ STp->ready = ST_READY;
if (STp->eof != ST_EOD) /* Save EOD across opens */
STp->eof = ST_NOEOF;
STp->eof_hit = 0;
@@ -435,7 +471,12 @@ scsi_tape_open(struct inode * inode, struct file * filp)
0, st_sleep_done, ST_LONG_TIMEOUT,
MAX_READY_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
@@ -449,7 +490,13 @@ scsi_tape_open(struct inode * inode, struct file * filp)
0, st_sleep_done, ST_LONG_TIMEOUT,
MAX_READY_RETRIES);
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
+
(STp->mt_status)->mt_fileno = STp->drv_block = 0;
STp->eof = ST_NOEOF;
}
@@ -459,14 +506,22 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
(STp->mt_status)->mt_fileno = STp->drv_block = 0 ;
printk("st%d: No tape.\n", dev);
+ STp->ready = ST_NO_TAPE;
} else {
- printk("st%d: Error %x.\n", dev, SCpnt->result);
(STp->mt_status)->mt_fileno = STp->drv_block = (-1);
+ STp->ready = ST_NOT_READY;
}
- (STp->buffer)->in_use = 0;
- STp->in_use = 0;
SCpnt->request.dev = -1; /* Mark as not busy */
- return (-EIO);
+ (STp->buffer)->in_use = 0;
+ STp->buffer = NULL;
+ STp->density = 0; /* Clear the erroneous "residue" */
+ STp->write_prot = 0;
+ STp->block_size = 0;
+ STp->eof = ST_NOEOF;
+ (STp->mt_status)->mt_fileno = STp->drv_block = 0;
+ if (scsi_tapes[dev].device->host->hostt->usage_count)
+ (*scsi_tapes[dev].device->host->hostt->usage_count)++;
+ return 0;
}
SCpnt->sense_buffer[0]=0;
@@ -477,7 +532,12 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(void *) cmd, (void *) (STp->buffer)->b_data,
6, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
STp->max_block = ((STp->buffer)->b_data[1] << 16) |
@@ -507,7 +567,12 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(void *) cmd, (void *) (STp->buffer)->b_data,
12, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
if ((STp->buffer)->last_result_fatal != 0) {
#ifdef DEBUG
@@ -533,10 +598,10 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
#ifdef DEBUG
if (debugging)
- printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n",
+ printk("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->block_size, STp->drv_buffer);
+ STp->drv_buffer);
#endif
if (STp->block_size > st_buffer_size) {
printk("st%d: Blocksize %d too large for buffer.\n", dev,
@@ -575,8 +640,17 @@ scsi_tape_open(struct inode * inode, struct file * filp)
if (debugging)
printk( "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 = 0;
+ STp->in_use = 0;
+ return (-EROFS);
+ }
}
+ if (scsi_tapes[dev].device->host->hostt->usage_count)
+ (*scsi_tapes[dev].device->host->hostt->usage_count)++;
+
return 0;
}
@@ -591,6 +665,7 @@ scsi_tape_close(struct inode * inode, struct file * filp)
static unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
+ unsigned int flags;
dev = MINOR(inode->i_rdev);
rewind = (dev & 0x80) == 0;
@@ -616,9 +691,15 @@ scsi_tape_close(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev;
scsi_do_cmd( SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data,
- 0, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ 0, st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
if ((STp->buffer)->last_result_fatal != 0) {
SCpnt->request.dev = -1; /* Mark as not busy */
@@ -626,7 +707,8 @@ scsi_tape_close(struct inode * inode, struct file * filp)
}
else {
SCpnt->request.dev = -1; /* Mark as not busy */
- (STp->mt_status)->mt_fileno++ ;
+ if ((STp->mt_status)->mt_fileno >= 0)
+ (STp->mt_status)->mt_fileno++ ;
STp->drv_block = 0;
if (STp->two_fm)
back_over_eof(dev);
@@ -651,9 +733,13 @@ scsi_tape_close(struct inode * inode, struct file * filp)
if (rewind)
st_int_ioctl(inode, filp, MTREW, 1);
- (STp->buffer)->in_use = 0;
+ if (STp->buffer != NULL)
+ (STp->buffer)->in_use = 0;
STp->in_use = 0;
+ if (scsi_tapes[dev].device->host->hostt->usage_count)
+ (*scsi_tapes[dev].device->host->hostt->usage_count)--;
+
return;
}
@@ -665,13 +751,17 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
int dev;
int total, do_count, blks, retval, transfer;
int write_threshold;
+ int doing_write = 0;
static unsigned char cmd[10];
char *b_point;
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
+ unsigned int flags;
dev = MINOR(inode->i_rdev) & 127;
STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
#ifdef DEBUG
if (!STp->in_use) {
printk("st%d: Incorrect device.\n", dev);
@@ -743,6 +833,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
(STp->block_size != 0 &&
(STp->buffer)->buffer_bytes + count > write_threshold))
{
+ doing_write = 1;
if (STp->block_size == 0)
do_count = count;
else {
@@ -767,9 +858,14 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
SCpnt->request.dev = dev;
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data, transfer,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
if ((STp->buffer)->last_result_fatal != 0) {
#ifdef DEBUG
@@ -850,7 +946,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
count = 0;
}
- if ((STp->buffer)->last_result_fatal != 0) {
+ if (doing_write && (STp->buffer)->last_result_fatal != 0) {
SCpnt->request.dev = -1;
return (STp->buffer)->last_result_fatal;
}
@@ -864,7 +960,8 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
else
(STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
STp->block_size) * STp->block_size;
- STp->dirty = 0;
+ STp->dirty = !((STp->buffer)->writing ==
+ (STp->buffer)->buffer_bytes);
if (STp->block_size == 0)
blks = (STp->buffer)->writing;
@@ -879,11 +976,12 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data,
(STp->buffer)->writing,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
}
else
SCpnt->request.dev = -1; /* Mark as not busy */
+ STp->at_sm &= (total != 0);
return( total);
}
@@ -898,9 +996,12 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
static unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
+ unsigned int flags;
dev = MINOR(inode->i_rdev) & 127;
STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
#ifdef DEBUG
if (!STp->in_use) {
printk("st%d: Incorrect device.\n", dev);
@@ -930,8 +1031,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
(STp->buffer)->buffer_bytes);
#endif
if (((STp->buffer)->buffer_bytes == 0) &&
- STp->eof == ST_EOM_OK) /* EOM or Blank Check */
- return (-EIO);
+ (STp->eof == ST_EOM_OK || STp->eof == ST_EOD))
+ return (-EIO); /* EOM or Blank Check */
STp->rw = ST_READING;
@@ -968,13 +1069,18 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
SCpnt->request.dev = dev;
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data,
- (STp->buffer)->buffer_size,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ bytes, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
(STp->buffer)->read_pointer = 0;
STp->eof_hit = 0;
+ STp->at_sm = 0;
if ((STp->buffer)->last_result_fatal) {
#ifdef DEBUG
@@ -1105,7 +1211,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
STp->drv_block = 0;
if (STp->moves_after_eof > 1)
STp->moves_after_eof = 0;
- (STp->mt_status)->mt_fileno++;
+ if ((STp->mt_status)->mt_fileno >= 0)
+ (STp->mt_status)->mt_fileno++;
}
if (total == 0 && STp->eof == ST_EOM_OK)
return (-EIO); /* ST_EOM_ERROR not used in read */
@@ -1138,14 +1245,15 @@ st_set_options(struct inode * inode, long options)
STp->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
STp->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;
#ifdef DEBUG
debugging = (options & MT_ST_DEBUGGING) != 0;
printk(
"st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
dev, STp->do_buffer_writes, STp->do_async_writes,
STp->do_read_ahead);
- printk(" two FMs: %d, debugging: %d\n", STp->two_fm,
- debugging);
+ printk(" two FMs: %d, fast mteom: %d debugging: %d\n",
+ STp->two_fm, STp->fast_mteom, debugging);
#endif
}
else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
@@ -1180,12 +1288,16 @@ st_int_ioctl(struct inode * inode,struct file * file,
unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
- int fileno, blkno, undone, datalen;
+ int fileno, blkno, at_sm, undone, datalen;
+ unsigned int flags;
dev = dev & 127;
STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
fileno = (STp->mt_status)->mt_fileno ;
blkno = STp->drv_block;
+ at_sm = STp->at_sm;
memset(cmd, 0, 10);
datalen = 0;
@@ -1202,8 +1314,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif
- fileno += arg;
+ if (fileno >= 0)
+ fileno += arg;
blkno = 0;
+ at_sm &= (arg != 0);
break;
case MTBSF:
case MTBSFM:
@@ -1221,10 +1335,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp));
}
#endif
- fileno -= arg;
+ if (fileno >= 0)
+ fileno -= arg;
blkno = (-1); /* We can't know the block number */
+ at_sm &= (arg != 0);
break;
- case MTFSR:
+ case MTFSR:
cmd[0] = SPACE;
cmd[1] = 0x00; /* Space Blocks */
cmd[2] = (arg >> 16);
@@ -1237,6 +1353,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
#endif
if (blkno >= 0)
blkno += arg;
+ at_sm &= (arg != 0);
break;
case MTBSR:
cmd[0] = SPACE;
@@ -1255,8 +1372,9 @@ st_int_ioctl(struct inode * inode,struct file * file,
#endif
if (blkno >= 0)
blkno -= arg;
+ at_sm &= (arg != 0);
break;
- case MTFSS:
+ case MTFSS:
cmd[0] = SPACE;
cmd[1] = 0x04; /* Space Setmarks */
cmd[2] = (arg >> 16);
@@ -1267,8 +1385,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape forward %d setmarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif
- if (arg != 0)
+ if (arg != 0) {
blkno = fileno = (-1);
+ at_sm = 1;
+ }
break;
case MTBSS:
cmd[0] = SPACE;
@@ -1285,8 +1405,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape backward %ld setmarks.\n", dev, (-ltmp));
}
#endif
- if (arg != 0)
+ if (arg != 0) {
blkno = fileno = (-1);
+ at_sm = 1;
+ }
break;
case MTWEOF:
case MTWSM:
@@ -1309,8 +1431,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
}
#endif
- fileno += arg;
+ if (fileno >= 0)
+ fileno += arg;
blkno = 0;
+ at_sm = (cmd_in == MTWSM);
break;
case MTREW:
cmd[0] = REZERO_UNIT;
@@ -1322,7 +1446,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
if (debugging)
printk("st%d: Rewinding tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0 ;
break;
case MTOFFL:
cmd[0] = START_STOP;
@@ -1334,7 +1458,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
if (debugging)
printk("st%d: Unloading tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0 ;
break;
case MTNOP:
#ifdef DEBUG
@@ -1354,34 +1478,47 @@ st_int_ioctl(struct inode * inode,struct file * file,
if (debugging)
printk("st%d: Retensioning tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0;
break;
case MTEOM:
- /* space to the end of tape */
- ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
- fileno = (STp->mt_status)->mt_fileno ;
- /* 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 */
+ if (!STp->fast_mteom) {
+ /* space to the end of tape */
+ ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
+ fileno = (STp->mt_status)->mt_fileno ;
+ if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
+ 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;
#ifdef DEBUG
if (debugging)
printk("st%d: Spacing to end of recorded medium.\n", dev);
#endif
- blkno = (-1);
+ 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 */
+#ifdef ST_NOWAIT
+ cmd[1] |= 2; /* Don't wait for completion */
+ timeout = ST_TIMEOUT;
+#else
+ timeout = ST_LONG_TIMEOUT * 8;
+#endif
#ifdef DEBUG
if (debugging)
printk("st%d: Erasing tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0 ;
break;
case MTSEEK:
if ((STp->device)->scsi_level < SCSI_2) {
@@ -1408,6 +1545,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Seeking tape to block %ld.\n", dev, arg);
#endif
fileno = blkno = (-1);
+ at_sm = 0;
break;
case MTSETBLK: /* Set block length */
case MTSETDENSITY: /* Set tape density */
@@ -1471,23 +1609,32 @@ st_int_ioctl(struct inode * inode,struct file * file,
(void *) cmd, (void *) (STp->buffer)->b_data, datalen,
st_sleep_done, timeout, MAX_RETRIES);
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
ioctl_result = (STp->buffer)->last_result_fatal;
SCpnt->request.dev = -1; /* Mark as not busy */
- if (!ioctl_result) {
+ if (cmd_in == MTFSF)
+ STp->moves_after_eof = 0;
+ else
+ STp->moves_after_eof = 1;
+ if (!ioctl_result) { /* SCSI command successful */
if (cmd_in != MTSEEK) {
STp->drv_block = blkno;
(STp->mt_status)->mt_fileno = fileno;
+ STp->at_sm = at_sm;
}
- else
+ else {
STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
- if (cmd_in == MTFSF)
- STp->moves_after_eof = 0;
- else
- STp->moves_after_eof = 1;
+ STp->at_sm = 0;
+ }
if (cmd_in == MTBSFM)
ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
else if (cmd_in == MTFSFM)
@@ -1512,16 +1659,18 @@ st_int_ioctl(struct inode * inode,struct file * file,
else if (cmd_in == MTSETDENSITY)
STp->density = arg;
else if (cmd_in == MTEOM) {
- STp->eof = ST_EOM_OK;
+ STp->eof = ST_EOD;
STp->eof_hit = 0;
}
else if (cmd_in != MTSETBLK && cmd_in != MTNOP) {
STp->eof = ST_NOEOF;
STp->eof_hit = 0;
}
- } else {
+ } else { /* SCSI command was not completely successful */
if (SCpnt->sense_buffer[2] & 0x40) {
- STp->eof = ST_EOM_OK;
+ if (cmd_in != MTBSF && cmd_in != MTBSFM &&
+ cmd_in != MTBSR && cmd_in != MTBSS)
+ STp->eof = ST_EOM_OK;
STp->eof_hit = 0;
STp->drv_block = 0;
}
@@ -1530,21 +1679,44 @@ st_int_ioctl(struct inode * inode,struct file * file,
(SCpnt->sense_buffer[4] << 16) +
(SCpnt->sense_buffer[5] << 8) +
SCpnt->sense_buffer[6] );
- if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) )
- (STp->mt_status)->mt_fileno = fileno - undone ;
- else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) )
+ if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
+ if (fileno >= 0)
+ (STp->mt_status)->mt_fileno = fileno - undone ;
+ else
+ (STp->mt_status)->mt_fileno = fileno;
+ STp->drv_block = 0;
+ }
+ else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
(STp->mt_status)->mt_fileno = fileno + undone ;
+ STp->drv_block = 0;
+ }
else if (cmd_in == MTFSR) {
- if (blkno >= undone)
- STp->drv_block = blkno - undone;
- else
- STp->drv_block = (-1);
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ (STp->mt_status)->mt_fileno++;
+ STp->drv_block = 0;
+ }
+ else {
+ if (blkno >= undone)
+ STp->drv_block = blkno - undone;
+ else
+ STp->drv_block = (-1);
+ }
}
- else if (cmd_in == MTBSR && blkno >= 0) {
- if (blkno >= 0)
- STp->drv_block = blkno + undone;
- else
+ else if (cmd_in == MTBSR) {
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ (STp->mt_status)->mt_fileno--;
STp->drv_block = (-1);
+ }
+ else {
+ if (blkno >= 0)
+ STp->drv_block = blkno + undone;
+ else
+ STp->drv_block = (-1);
+ }
+ }
+ else if (cmd_in == MTEOM || cmd_in == MTSEEK) {
+ (STp->mt_status)->mt_fileno = (-1);
+ STp->drv_block = (-1);
}
if (STp->eof == ST_NOEOF &&
(SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
@@ -1568,6 +1740,7 @@ st_ioctl(struct inode * inode,struct file * file,
unsigned char scmd[10];
Scsi_Cmnd *SCpnt;
Scsi_Tape *STp;
+ unsigned int flags;
dev = dev & 127;
STp = &(scsi_tapes[dev]);
@@ -1590,11 +1763,15 @@ st_ioctl(struct inode * inode,struct file * file,
memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
- i = flush_buffer(inode, file, mtc.mt_op == MTNOP || mtc.mt_op == MTSEEK ||
+ i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM);
if (i < 0)
return i;
+ if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+ mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+ mtc.mt_op != MTSETDRVBUFFER)
+ STp->rw = ST_IDLE; /* Prevent automatic WEOF */
if (mtc.mt_op == MTSETDRVBUFFER &&
(mtc.mt_count & MT_ST_OPTIONS) != 0)
@@ -1642,6 +1819,12 @@ st_ioctl(struct inode * inode,struct file * file,
(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 (STp->at_sm)
+ (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
memcpy_tofs((char *)arg, (char *)(STp->mt_status),
sizeof(struct mtget));
@@ -1650,6 +1833,8 @@ st_ioctl(struct inode * inode,struct file * file,
return 0;
}
else if (cmd == (MTIOCPOS & IOCCMD_MASK)) {
+ if (STp->ready != ST_READY)
+ return (-EIO);
#ifdef DEBUG
if (debugging)
printk("st%d: get tape position.\n", dev);
@@ -1683,7 +1868,13 @@ st_ioctl(struct inode * inode,struct file * file,
(void *) scmd, (void *) (STp->buffer)->b_data,
20, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
if ((STp->buffer)->last_result_fatal != 0) {
mt_pos.mt_blkno = (-1);
@@ -1712,8 +1903,10 @@ st_ioctl(struct inode * inode,struct file * file,
memcpy_tofs((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
return result;
}
- else
+ else if (STp->ready == ST_READY)
return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+ else
+ return (-EIO);
}
@@ -1748,16 +1941,17 @@ static struct file_operations st_fops = {
NULL /* fsync */
};
-static void st_attach(Scsi_Device * SDp){
+static int st_attach(Scsi_Device * SDp){
Scsi_Tape * tpnt;
int i;
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
- if(SDp->type != TYPE_TAPE) return;
+ if(SDp->type != TYPE_TAPE) return 1;
if(st_template.nr_dev >= st_template.dev_max)
- panic ("scsi_devices corrupt (st)");
+ {
+ SDp->attached--;
+ return 1;
+ }
for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++)
if(!tpnt->device) break;
@@ -1765,13 +1959,17 @@ static void st_attach(Scsi_Device * SDp){
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;
+
st_template.nr_dev++;
+ return 0;
};
-static int st_detect(Scsi_Device * SDp){
-
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return 0;
+static int st_detect(Scsi_Device * SDp)
+{
if(SDp->type != TYPE_TAPE) return 0;
printk("Detected scsi tape st%d at scsi%d, id %d, lun %d\n",
@@ -1786,7 +1984,6 @@ static void st_init()
{
int i;
Scsi_Tape * STp;
- Scsi_Device * SDp;
static int st_registered = 0;
if (st_template.dev_noticed == 0) return;
@@ -1799,19 +1996,18 @@ static void st_init()
st_registered++;
}
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
-
- scsi_tapes = (Scsi_Tape *) scsi_init_malloc(st_template.dev_noticed *
- sizeof(Scsi_Tape));
- st_template.dev_max = st_template.dev_noticed;
+ if (scsi_tapes) return;
+ scsi_tapes = (Scsi_Tape *) scsi_init_malloc(
+ (st_template.dev_noticed + ST_EXTRA_DEVS) *
+ sizeof(Scsi_Tape), GFP_ATOMIC);
+ st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
#ifdef DEBUG
printk("st: Buffer size %d bytes, write threshold %d bytes.\n",
st_buffer_size, st_write_threshold);
#endif
- for (i=0, SDp = scsi_devices; i < st_template.dev_noticed; ++i) {
+ for (i=0; i < st_template.dev_max; ++i) {
STp = &(scsi_tapes[i]);
STp->device = NULL;
STp->capacity = 0xfffff;
@@ -1826,35 +2022,30 @@ static void st_init()
STp->do_async_writes = ST_ASYNC_WRITES;
STp->do_read_ahead = ST_READ_AHEAD;
STp->two_fm = ST_TWO_FM;
+ STp->fast_mteom = ST_FAST_MTEOM;
STp->write_threshold = st_write_threshold;
STp->drv_block = 0;
STp->moves_after_eof = 1;
- STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget));
+ STp->at_sm = 0;
+ 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));
- for (; SDp; SDp = SDp->next)
- if (SDp->type == TYPE_TAPE)
- break;
- if (!SDp)
- printk("st%d: ERROR: Not found in scsi chain.\n", i);
- else {
- if (SDp->scsi_level <= 2)
- STp->mt_status->mt_type = MT_ISSCSI1;
- else
- STp->mt_status->mt_type = MT_ISSCSI2;
- }
- SDp = SDp->next;
}
/* Allocate the buffers */
- st_nbr_buffers = st_template.dev_noticed;
+ st_nbr_buffers = st_template.dev_noticed + ST_EXTRA_DEVS;
if (st_nbr_buffers > st_max_buffers)
st_nbr_buffers = st_max_buffers;
st_buffers = (ST_buffer **) scsi_init_malloc(st_nbr_buffers *
- sizeof(ST_buffer *));
+ sizeof(ST_buffer *), GFP_ATOMIC);
+ /* FIXME - if we are hitting this because we are loading a tape module
+ as a loadable driver, we should not use kmalloc - it will allocate
+ a 64Kb region in order to buffer about 32Kb. Try using 31 blocks
+ instead. */
+
for (i=0; i < st_nbr_buffers; i++) {
st_buffers[i] = (ST_buffer *) scsi_init_malloc(sizeof(ST_buffer) -
- 1 + st_buffer_size);
+ 1 + st_buffer_size, GFP_ATOMIC | GFP_DMA);
#ifdef DEBUG
/* printk("st: Buffer address: %p\n", st_buffers[i]); */
#endif
@@ -1863,3 +2054,19 @@ static void st_init()
}
return;
}
+
+static void st_detach(Scsi_Device * SDp)
+{
+ 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;
+}