blob: ba81dc7f682e0b0d0f370952488f28efdba57e55 [file] [log] [blame]
/*
SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
file Documentation/scsi/st.txt 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, Eugene Exarevsky, Eric Lee Green, Wolfgang Denk,
Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
Michael Schaefer, J"org Weule, and Eric Youngdale.
Copyright 1992 - 2003 Kai Makisara
email Kai.Makisara@kolumbus.fi
Last modified: Sun Apr 13 10:17:18 2003 by makisara
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
*/
static char *verstr = "20030413";
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/mtio.h>
#include <linux/ioctl.h>
#include <linux/fcntl.h>
#include <linux/spinlock.h>
#include <linux/blk.h>
#include <linux/moduleparam.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#include <asm/system.h>
/* The driver prints some debugging information on the console if DEBUG
is defined and non-zero. */
#define DEBUG 0
#if DEBUG
/* The message level for the debug messages is currently set to KERN_NOTICE
so that people can easily see the messages. Later when the debugging messages
in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
#define ST_DEB_MSG KERN_NOTICE
#define DEB(a) a
#define DEBC(a) if (debugging) { a ; }
#else
#define DEB(a)
#define DEBC(a)
#endif
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsi_ioctl.h>
#define ST_KILOBYTE 1024
#include "st_options.h"
#include "st.h"
static int buffer_kbs;
static int max_sg_segs;
static int try_direct_io = TRY_DIRECT_IO;
static int try_rdio = TRUE;
static int try_wdio = TRUE;
static int st_dev_max;
static int st_nr_dev;
MODULE_AUTHOR("Kai Makisara");
MODULE_DESCRIPTION("SCSI Tape Driver");
MODULE_LICENSE("GPL");
/* Set 'perm' (4th argument) to 0 to disable module_param's definition
* of sysfs parameters (which module_param doesn't yet support).
* Sysfs parameters defined explicitly later.
*/
module_param_named(buffer_kbs, buffer_kbs, int, 0);
MODULE_PARM_DESC(buffer_kbs, "Default driver buffer size for fixed block mode (KB; 32)");
module_param_named(max_sg_segs, max_sg_segs, int, 0);
MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (256)");
module_param_named(try_direct_io, try_direct_io, int, 0);
MODULE_PARM_DESC(try_direct_io, "Try direct I/O between user buffer and tape drive (1)");
/* Extra parameters for testing */
module_param_named(try_rdio, try_rdio, int, 0);
MODULE_PARM_DESC(try_rdio, "Try direct read i/o when possible");
module_param_named(try_wdio, try_wdio, int, 0);
MODULE_PARM_DESC(try_wdio, "Try direct write i/o when possible");
#ifndef MODULE
static int write_threshold_kbs; /* retained for compatibility */
static struct st_dev_parm {
char *name;
int *val;
} parms[] __initdata = {
{
"buffer_kbs", &buffer_kbs
},
{ /* Retained for compatibility with 2.4 */
"write_threshold_kbs", &write_threshold_kbs
},
{
"max_sg_segs", NULL
},
{
"try_direct_io", &try_direct_io
}
};
#endif
static char *st_formats[ST_NBR_MODES] ={"", "l", "m", "a"};
/* The default definitions have been moved to st_options.h */
#define ST_FIXED_BUFFER_SIZE (ST_FIXED_BUFFER_BLOCKS * ST_KILOBYTE)
/* The buffer size should fit into the 24 bits for length in the
6-byte SCSI read and write commands. */
#if ST_FIXED_BUFFER_SIZE >= (2 << 24 - 1)
#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
#endif
DEB( static int debugging = DEBUG; )
#define MAX_RETRIES 0
#define MAX_WRITE_RETRIES 0
#define MAX_READY_RETRIES 0
#define NO_TAPE NOT_READY
#define ST_TIMEOUT (900 * HZ)
#define ST_LONG_TIMEOUT (14000 * HZ)
#define TAPE_NR(x) (minor(x) & ~(-1 << ST_MODE_SHIFT))
#define TAPE_MODE(x) ((minor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
24 bits) */
#define SET_DENS_AND_BLK 0x10001
#define ST_DEV_ARR_LUMP 6
static rwlock_t st_dev_arr_lock = RW_LOCK_UNLOCKED;
static int st_fixed_buffer_size = ST_FIXED_BUFFER_SIZE;
static int st_max_sg_segs = ST_MAX_SG;
static Scsi_Tape **scsi_tapes = NULL;
static int modes_defined;
static ST_buffer *new_tape_buffer(int, int, int);
static int enlarge_buffer(ST_buffer *, int, int);
static void normalize_buffer(ST_buffer *);
static int append_to_buffer(const char *, ST_buffer *, int);
static int from_buffer(ST_buffer *, char *, int);
static void move_buffer_data(ST_buffer *, int);
static void buf_to_sg(ST_buffer *, unsigned int);
static int st_map_user_pages(struct scatterlist *, const unsigned int,
unsigned long, size_t, int, unsigned long);
static int sgl_map_user_pages(struct scatterlist *, const unsigned int,
unsigned long, size_t, int);
static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int);
static int st_attach(Scsi_Device *);
static void st_detach(Scsi_Device *);
static void do_create_driverfs_files(void);
static void do_remove_driverfs_files(void);
static struct Scsi_Device_Template st_template = {
.module = THIS_MODULE,
.list = LIST_HEAD_INIT(st_template.list),
.name = "tape",
.scsi_type = TYPE_TAPE,
.attach = st_attach,
.detach = st_detach,
.scsi_driverfs_driver = {
.name = "st",
},
};
static int st_compression(Scsi_Tape *, int);
static int find_partition(Scsi_Tape *);
static int switch_partition(Scsi_Tape *);
static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
#include "osst_detect.h"
#ifndef SIGS_FROM_OSST
#define SIGS_FROM_OSST \
{"OnStream", "SC-", "", "osst"}, \
{"OnStream", "DI-", "", "osst"}, \
{"OnStream", "DP-", "", "osst"}, \
{"OnStream", "USB", "", "osst"}, \
{"OnStream", "FW-", "", "osst"}
#endif
struct st_reject_data {
char *vendor;
char *model;
char *rev;
char *driver_hint; /* Name of the correct driver, NULL if unknown */
};
static struct st_reject_data reject_list[] = {
/* {"XXX", "Yy-", "", NULL}, example */
SIGS_FROM_OSST,
{NULL, }};
/* If the device signature is on the list of incompatible drives, the
function returns a pointer to the name of the correct driver (if known) */
static char * st_incompatible(Scsi_Device* SDp)
{
struct st_reject_data *rp;
for (rp=&(reject_list[0]); rp->vendor != NULL; rp++)
if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
!strncmp(rp->model, SDp->model, strlen(rp->model)) &&
!strncmp(rp->rev, SDp->rev, strlen(rp->rev))) {
if (rp->driver_hint)
return rp->driver_hint;
else
return "unknown";
}
return NULL;
}
static inline char *tape_name(Scsi_Tape *tape)
{
return tape->disk->disk_name;
}
/* Convert the result to success code */
static int st_chk_result(Scsi_Tape *STp, Scsi_Request * SRpnt)
{
int result = SRpnt->sr_result;
unsigned char *sense = SRpnt->sr_sense_buffer, scode;
DEB(const char *stp;)
char *name = tape_name(STp);
if (!result) {
sense[0] = 0; /* We don't have sense data if this byte is zero */
return 0;
}
if ((driver_byte(result) & DRIVER_MASK) == DRIVER_SENSE)
scode = sense[2] & 0x0f;
else {
sense[0] = 0;
scode = 0;
}
DEB(
if (debugging) {
printk(ST_DEB_MSG "%s: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
name, result,
SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2],
SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5],
SRpnt->sr_bufflen);
if (driver_byte(result) & DRIVER_SENSE)
print_req_sense("st", SRpnt);
} else ) /* end DEB */
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 &&
SRpnt->sr_cmnd[0] != MODE_SENSE &&
SRpnt->sr_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
if (driver_byte(result) & DRIVER_SENSE) {
printk(KERN_WARNING "%s: Error with sense data: ", name);
print_req_sense("st", SRpnt);
} else
printk(KERN_WARNING
"%s: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
name, result, suggestion(result),
driver_byte(result) & DRIVER_MASK, host_byte(result));
}
if (STp->cln_mode >= EXTENDED_SENSE_START) {
if (STp->cln_sense_value)
STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] &
STp->cln_sense_mask) == STp->cln_sense_value);
else
STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] &
STp->cln_sense_mask) != 0);
}
if (sense[12] == 0 && sense[13] == 0x17) /* ASC and ASCQ => cleaning requested */
STp->cleaning_req = 1;
STp->pos_unknown |= STp->device->was_reset;
if ((sense[0] & 0x70) == 0x70 &&
scode == RECOVERED_ERROR
#if ST_RECOVERED_WRITE_FATAL
&& SRpnt->sr_cmnd[0] != WRITE_6
&& SRpnt->sr_cmnd[0] != WRITE_FILEMARKS
#endif
) {
STp->recover_count++;
STp->recover_reg++;
DEB(
if (debugging) {
if (SRpnt->sr_cmnd[0] == READ_6)
stp = "read";
else if (SRpnt->sr_cmnd[0] == WRITE_6)
stp = "write";
else
stp = "ioctl";
printk(ST_DEB_MSG "%s: Recovered %s error (%d).\n", name, stp,
STp->recover_count);
} ) /* end DEB */
if ((sense[2] & 0xe0) == 0)
return 0;
}
return (-EIO);
}
/* Wakeup from interrupt */
static void st_sleep_done(Scsi_Cmnd * SCpnt)
{
int remainder;
Scsi_Tape *STp = container_of(SCpnt->request->rq_disk->private_data,
Scsi_Tape, driver);
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)->midlevel_result = SCpnt->result; /* Error */
else
(STp->buffer)->midlevel_result = INT_MAX; /* OK */
} else
(STp->buffer)->midlevel_result = SCpnt->result;
SCpnt->request->rq_status = RQ_SCSI_DONE;
(STp->buffer)->last_SRpnt = SCpnt->sc_request;
DEB( STp->write_pending = 0; )
complete(SCpnt->request->waiting);
}
/* 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_Request *
st_do_scsi(Scsi_Request * SRpnt, Scsi_Tape * STp, unsigned char *cmd, int bytes,
int direction, int timeout, int retries, int do_wait)
{
unsigned char *bp;
if (SRpnt == NULL) {
SRpnt = scsi_allocate_request(STp->device);
if (SRpnt == NULL) {
DEBC( printk(KERN_ERR "%s: Can't get SCSI request.\n",
tape_name(STp)); );
if (signal_pending(current))
(STp->buffer)->syscall_result = (-EINTR);
else
(STp->buffer)->syscall_result = (-EBUSY);
return NULL;
}
}
init_completion(&STp->wait);
SRpnt->sr_use_sg = STp->buffer->do_dio || (bytes > (STp->buffer)->frp[0].length);
if (SRpnt->sr_use_sg) {
if (!STp->buffer->do_dio)
buf_to_sg(STp->buffer, bytes);
SRpnt->sr_use_sg = (STp->buffer)->sg_segs;
bp = (char *) &((STp->buffer)->sg[0]);
} else
bp = (STp->buffer)->b_data;
SRpnt->sr_data_direction = direction;
SRpnt->sr_cmd_len = 0;
SRpnt->sr_request->waiting = &(STp->wait);
SRpnt->sr_request->rq_status = RQ_SCSI_BUSY;
SRpnt->sr_request->rq_disk = STp->disk;
scsi_do_req(SRpnt, (void *) cmd, bp, bytes,
st_sleep_done, timeout, retries);
if (do_wait) {
wait_for_completion(SRpnt->sr_request->waiting);
SRpnt->sr_request->waiting = NULL;
(STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
}
return SRpnt;
}
/* Handle the write-behind checking (downs the semaphore) */
static void write_behind_check(Scsi_Tape * STp)
{
ST_buffer *STbuffer;
ST_partstat *STps;
STbuffer = STp->buffer;
DEB(
if (STp->write_pending)
STp->nbr_waits++;
else
STp->nbr_finished++;
) /* end DEB */
wait_for_completion(&(STp->wait));
(STp->buffer)->last_SRpnt->sr_request->waiting = NULL;
(STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt);
scsi_release_request((STp->buffer)->last_SRpnt);
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;
}
/* 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)
{
Scsi_Request *SRpnt;
unsigned char cmd[MAX_COMMAND_SIZE];
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;
DEBC(printk(ST_DEB_MSG "%s: Stepping over filemark %s.\n",
tape_name(STp), forward ? "forward" : "backward"));
SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE,
STp->timeout, MAX_RETRIES, TRUE);
if (!SRpnt)
return (STp->buffer)->syscall_result;
scsi_release_request(SRpnt);
SRpnt = NULL;
if ((STp->buffer)->midlevel_result != 0)
printk(KERN_ERR "%s: Stepping over filemark %s failed.\n",
tape_name(STp), forward ? "forward" : "backward");
return (STp->buffer)->syscall_result;
}
/* 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[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
ST_partstat *STps;
if ((STp->buffer)->writing) {
write_behind_check(STp);
if ((STp->buffer)->syscall_result) {
DEBC(printk(ST_DEB_MSG
"%s: Async write error (flush) %x.\n",
tape_name(STp), (STp->buffer)->midlevel_result))
if ((STp->buffer)->midlevel_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;
DEBC(printk(ST_DEB_MSG "%s: Flushing %d bytes.\n",
tape_name(STp), transfer));
memset((STp->buffer)->b_data + offset, 0, transfer - offset);
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = WRITE_6;
cmd[1] = 1;
blks = transfer / STp->block_size;
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
SRpnt = st_do_scsi(NULL, STp, cmd, transfer, SCSI_DATA_WRITE,
STp->timeout, MAX_WRITE_RETRIES, TRUE);
if (!SRpnt)
return (STp->buffer)->syscall_result;
STps = &(STp->ps[STp->partition]);
if ((STp->buffer)->syscall_result != 0) {
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
(SRpnt->sr_sense_buffer[2] & 0x40) &&
(SRpnt->sr_sense_buffer[2] & 0x0f) == NO_SENSE) {
STp->dirty = 0;
(STp->buffer)->buffer_bytes = 0;
result = (-ENOSPC);
} else {
printk(KERN_ERR "%s: Error on flush.\n",
tape_name(STp));
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_request(SRpnt);
SRpnt = NULL;
}
return result;
}
/* Flush the tape buffer. The tape will be positioned correctly unless
seek_next is true. */
static int flush_buffer(Scsi_Tape *STp, int seek_next)
{
int backspace, result;
ST_buffer *STbuffer;
ST_partstat *STps;
STbuffer = STp->buffer;
/*
* If there was a bus reset, block further access
* to this device.
*/
if (STp->pos_unknown)
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(STp, 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(Scsi_Tape * STp, ST_mode * STm)
{
int set_it = FALSE;
unsigned long arg;
char *name = tape_name(STp);
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(STp, SET_DENS_AND_BLK, arg)) {
printk(KERN_WARNING
"%s: Can't set default block size to %d bytes and density %x.\n",
name, STm->default_blksize, STm->default_density);
if (modes_defined)
return (-EINVAL);
}
return 0;
}
/* Lock or unlock the drive door. Don't use when Scsi_Request allocated. */
static int do_door_lock(Scsi_Tape * STp, int do_lock)
{
int retval, cmd;
DEB(char *name = tape_name(STp);)
cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK;
DEBC(printk(ST_DEB_MSG "%s: %socking drive door.\n", name,
do_lock ? "L" : "Unl"));
retval = scsi_ioctl(STp->device, cmd, NULL);
if (!retval) {
STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
}
else {
STp->door_locked = ST_LOCK_FAILS;
}
return retval;
}
/* Set the internal state after reset */
static void reset_state(Scsi_Tape *STp)
{
int i;
ST_partstat *STps;
STp->pos_unknown = 0;
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 = -1;
STps->drv_file = -1;
}
if (STp->can_partitions) {
STp->partition = find_partition(STp);
if (STp->partition < 0)
STp->partition = 0;
STp->new_partition = STp->partition;
}
}
/* Test if the drive is ready. Returns either one of the codes below or a negative system
error code. */
#define CHKRES_READY 0
#define CHKRES_NEW_SESSION 1
#define CHKRES_NOT_READY 2
#define CHKRES_NO_TAPE 3
#define MAX_ATTENTIONS 10
static int test_ready(Scsi_Tape *STp, int do_wait)
{
int attentions, waits, max_wait, scode;
int retval = CHKRES_READY, new_session = FALSE;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt = NULL;
max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
for (attentions=waits=0; ; ) {
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = TEST_UNIT_READY;
SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
STp->long_timeout, MAX_READY_RETRIES, TRUE);
if (!SRpnt) {
retval = (STp->buffer)->syscall_result;
break;
}
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70) {
scode = (SRpnt->sr_sense_buffer[2] & 0x0f);
if (scode == UNIT_ATTENTION) { /* New media? */
new_session = TRUE;
if (attentions < MAX_ATTENTIONS) {
attentions++;
continue;
}
else {
retval = (-EIO);
break;
}
}
if (scode == NOT_READY) {
if (waits < max_wait) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
if (signal_pending(current)) {
retval = (-EINTR);
break;
}
waits++;
continue;
}
else {
if ((STp->device)->scsi_level >= SCSI_2 &&
SRpnt->sr_sense_buffer[12] == 0x3a) /* Check ASC */
retval = CHKRES_NO_TAPE;
else
retval = CHKRES_NOT_READY;
break;
}
}
}
retval = (STp->buffer)->syscall_result;
if (!retval)
retval = new_session ? CHKRES_NEW_SESSION : CHKRES_READY;
break;
}
if (SRpnt != NULL)
scsi_release_request(SRpnt);
return retval;
}
/* See if the drive is ready and gather information about the tape. Return values:
< 0 negative error code from errno.h
0 drive ready
1 drive not ready (possibly no tape)
*/
static int check_tape(Scsi_Tape *STp, struct file *filp)
{
int i, retval, new_session = FALSE, do_wait;
unsigned char cmd[MAX_COMMAND_SIZE], saved_cleaning;
unsigned short st_flags = filp->f_flags;
Scsi_Request *SRpnt = NULL;
ST_mode *STm;
ST_partstat *STps;
char *name = tape_name(STp);
struct inode *inode = filp->f_dentry->d_inode;
int mode = TAPE_MODE(inode->i_rdev);
STp->ready = ST_READY;
if (mode != STp->current_mode) {
DEBC(printk(ST_DEB_MSG "%s: Mode change from %d to %d.\n",
name, STp->current_mode, mode));
new_session = TRUE;
STp->current_mode = mode;
}
STm = &(STp->modes[STp->current_mode]);
saved_cleaning = STp->cleaning_req;
STp->cleaning_req = 0;
do_wait = ((filp->f_flags & O_NONBLOCK) == 0);
retval = test_ready(STp, do_wait);
if (retval < 0)
goto err_out;
if (retval == CHKRES_NEW_SESSION) {
STp->pos_unknown = 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;
}
else {
STp->cleaning_req |= saved_cleaning;
if (retval == CHKRES_NOT_READY || retval == CHKRES_NO_TAPE) {
if (retval == CHKRES_NO_TAPE)
STp->ready = ST_NO_TAPE;
else
STp->ready = ST_NOT_READY;
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;
return CHKRES_NOT_READY;
}
}
if (STp->omit_blklims)
STp->min_block = STp->max_block = (-1);
else {
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = READ_BLOCK_LIMITS;
SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, SCSI_DATA_READ, STp->timeout,
MAX_READY_RETRIES, TRUE);
if (!SRpnt) {
retval = (STp->buffer)->syscall_result;
goto err_out;
}
if (!SRpnt->sr_result && !SRpnt->sr_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 ( DEB( debugging || ) !STp->inited)
printk(KERN_WARNING
"%s: Block limits %d - %d bytes.\n", name,
STp->min_block, STp->max_block);
} else {
STp->min_block = STp->max_block = (-1);
DEBC(printk(ST_DEB_MSG "%s: Can't read block limits.\n",
name));
}
}
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SENSE;
cmd[4] = 12;
SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, SCSI_DATA_READ, STp->timeout,
MAX_READY_RETRIES, TRUE);
if (!SRpnt) {
retval = (STp->buffer)->syscall_result;
goto err_out;
}
if ((STp->buffer)->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "%s: No Mode Sense.\n", name));
STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */
(STp->buffer)->syscall_result = 0; /* Prevent error propagation */
STp->drv_write_prot = 0;
} else {
DEBC(printk(ST_DEB_MSG
"%s: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n",
name,
(STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
(STp->buffer)->b_data[2], (STp->buffer)->b_data[3]));
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];
DEBC(printk(ST_DEB_MSG
"%s: Density %x, tape length: %x, drv buffer: %d\n",
name, STp->density, (STp->buffer)->b_data[5] * 65536 +
(STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
STp->drv_buffer));
}
STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
}
scsi_release_request(SRpnt);
SRpnt = NULL;
STp->inited = TRUE;
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;
DEBC(printk(ST_DEB_MSG
"%s: Block size: %d, buffer size: %d (%d blocks).\n", name,
STp->block_size, (STp->buffer)->buffer_size,
(STp->buffer)->buffer_blocks));
if (STp->drv_write_prot) {
STp->write_prot = 1;
DEBC(printk(ST_DEB_MSG "%s: Write protected\n", name));
if (do_wait &&
((st_flags & O_ACCMODE) == O_WRONLY ||
(st_flags & O_ACCMODE) == O_RDWR)) {
retval = (-EROFS);
goto err_out;
}
}
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. */
DEBC(printk(ST_DEB_MSG
"%s: Updating partition number in status.\n", name));
if ((STp->partition = find_partition(STp)) < 0) {
retval = STp->partition;
goto err_out;
}
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) &&
(retval = set_mode_densblk(STp, STm)) < 0)
goto err_out;
if (STp->default_drvbuffer != 0xff) {
if (st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer))
printk(KERN_WARNING
"%s: Can't set default drive buffering to %d.\n",
name, STp->default_drvbuffer);
}
}
return CHKRES_READY;
err_out:
return retval;
}
/* Open the device. Needs to be called with BKL only because of incrementing the SCSI host
module count. */
static int st_open(struct inode *inode, struct file *filp)
{
int i, retval = (-EIO);
Scsi_Tape *STp;
ST_partstat *STps;
int dev = TAPE_NR(inode->i_rdev);
char *name;
write_lock(&st_dev_arr_lock);
if (dev >= st_dev_max || scsi_tapes == NULL ||
((STp = scsi_tapes[dev]) == NULL)) {
write_unlock(&st_dev_arr_lock);
return (-ENXIO);
}
filp->private_data = STp;
name = tape_name(STp);
if (STp->in_use) {
write_unlock(&st_dev_arr_lock);
DEB( printk(ST_DEB_MSG "%s: Device already in use.\n", name); )
return (-EBUSY);
}
if(scsi_device_get(STp->device)) {
write_unlock(&st_dev_arr_lock);
return (-ENXIO);
}
STp->in_use = 1;
write_unlock(&st_dev_arr_lock);
STp->rew_at_close = STp->autorew_dev = (minor(inode->i_rdev) & 0x80) == 0;
if (!scsi_block_when_processing_errors(STp->device)) {
retval = (-ENXIO);
goto err_out;
}
/* See that we have at least a one page buffer available */
if (!enlarge_buffer(STp->buffer, PAGE_SIZE, STp->restr_dma)) {
printk(KERN_WARNING "%s: Can't allocate tape buffer.\n", name);
retval = (-EOVERFLOW);
goto err_out;
}
(STp->buffer)->writing = 0;
(STp->buffer)->syscall_result = 0;
STp->write_prot = ((filp->f_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->recover_count = 0;
DEB( STp->nbr_waits = STp->nbr_finished = 0;
STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = STp->nbr_combinable = 0; )
retval = check_tape(STp, filp);
if (retval < 0)
goto err_out;
if ((filp->f_flags & O_NONBLOCK) == 0 &&
retval != CHKRES_READY) {
retval = (-EIO);
goto err_out;
}
return 0;
err_out:
normalize_buffer(STp->buffer);
STp->in_use = 0;
scsi_device_put(STp->device);
return retval;
}
/* Flush the tape buffer before close */
static int st_flush(struct file *filp)
{
int result = 0, result2;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
Scsi_Tape *STp = filp->private_data;
ST_mode *STm = &(STp->modes[STp->current_mode]);
ST_partstat *STps = &(STp->ps[STp->partition]);
char *name = tape_name(STp);
if (file_count(filp) > 1)
return 0;
if (STps->rw == ST_WRITING && !STp->pos_unknown) {
result = flush_write_buffer(STp);
if (result != 0 && result != (-ENOSPC))
goto out;
}
if (STp->can_partitions &&
(result2 = switch_partition(STp)) < 0) {
DEBC(printk(ST_DEB_MSG
"%s: switch_partition at close failed.\n", name));
if (result == 0)
result = result2;
goto out;
}
DEBC( if (STp->nbr_requests)
printk(KERN_WARNING "%s: Number of r/w requests %d, dio used in %d, pages %d (%d).\n",
name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages, STp->nbr_combinable));
if (STps->rw == ST_WRITING && !STp->pos_unknown) {
DEBC(printk(ST_DEB_MSG "%s: File length %ld bytes.\n",
name, (long) (filp->f_pos));
printk(ST_DEB_MSG "%s: Async write waits %d, finished %d.\n",
name, STp->nbr_waits, STp->nbr_finished);
)
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = WRITE_FILEMARKS;
cmd[4] = 1 + STp->two_fm;
SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE,
STp->timeout, MAX_WRITE_RETRIES, TRUE);
if (!SRpnt) {
result = (STp->buffer)->syscall_result;
goto out;
}
if ((STp->buffer)->syscall_result != 0 &&
((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
(SRpnt->sr_sense_buffer[2] & 0x4f) != 0x40 ||
((SRpnt->sr_sense_buffer[0] & 0x80) != 0 &&
(SRpnt->sr_sense_buffer[3] | SRpnt->sr_sense_buffer[4] |
SRpnt->sr_sense_buffer[5] |
SRpnt->sr_sense_buffer[6]) != 0))) {
/* Filter out successful write at EOM */
scsi_release_request(SRpnt);
SRpnt = NULL;
printk(KERN_ERR "%s: Error on write filemark.\n", name);
if (result == 0)
result = (-EIO);
} else {
scsi_release_request(SRpnt);
SRpnt = 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;
}
DEBC(printk(ST_DEB_MSG "%s: Buffer flushed, %d EOF(s) written\n",
name, cmd[4]));
} 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(STp, 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(STp, MTREW, 1);
if (result == 0)
result = result2;
}
return result;
}
/* Close the device and release it. BKL is not needed: this is the only thread
accessing this tape. */
static int st_release(struct inode *inode, struct file *filp)
{
int result = 0;
Scsi_Tape *STp = filp->private_data;
if (STp->door_locked == ST_LOCKED_AUTO)
do_door_lock(STp, 0);
normalize_buffer(STp->buffer);
write_lock(&st_dev_arr_lock);
STp->in_use = 0;
write_unlock(&st_dev_arr_lock);
scsi_device_put(STp->device);
return result;
}
/* The checks common to both reading and writing */
static ssize_t rw_checks(Scsi_Tape *STp, struct file *filp, size_t count, loff_t *ppos)
{
ssize_t retval = 0;
/*
* 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)) {
retval = (-ENXIO);
goto out;
}
if (ppos != &filp->f_pos) {
/* "A request was outside the capabilities of the device." */
retval = (-ENXIO);
goto out;
}
if (STp->ready != ST_READY) {
if (STp->ready == ST_NO_TAPE)
retval = (-ENOMEDIUM);
else
retval = (-EIO);
goto out;
}
if (! STp->modes[STp->current_mode].defined) {
retval = (-ENXIO);
goto out;
}
/*
* If there was a bus reset, block further access
* to this device.
*/
if (STp->pos_unknown) {
retval = (-EIO);
goto out;
}
if (count == 0)
goto out;
DEB(
if (!STp->in_use) {
printk(ST_DEB_MSG "%s: Incorrect device.\n", tape_name(STp));
retval = (-EIO);
goto out;
} ) /* end DEB */
if (STp->can_partitions &&
(retval = switch_partition(STp)) < 0)
goto out;
if (STp->block_size == 0 && STp->max_block > 0 &&
(count < STp->min_block || count > STp->max_block)) {
retval = (-EINVAL);
goto out;
}
if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
!do_door_lock(STp, 1))
STp->door_locked = ST_LOCKED_AUTO;
out:
return retval;
}
static int setup_buffering(Scsi_Tape *STp, const char *buf, size_t count, int is_read)
{
int i, bufsize, retval = 0;
ST_buffer *STbp = STp->buffer;
if (is_read)
i = STp->try_dio && try_rdio;
else
i = STp->try_dio && try_wdio;
if (i) {
i = st_map_user_pages(&(STbp->sg[0]), STbp->use_sg,
(unsigned long)buf, count, (is_read ? READ : WRITE),
STp->max_pfn);
if (i > 0) {
STbp->do_dio = i;
STbp->buffer_bytes = 0; /* can be used as transfer counter */
}
else
STbp->do_dio = FALSE; /* fall back to buffering with any error */
STbp->sg_segs = STbp->do_dio;
STbp->frp_sg_current = 0;
DEB(
if (STbp->do_dio) {
STp->nbr_dio++;
STp->nbr_pages += STbp->do_dio;
for (i=1; i < STbp->do_dio; i++)
if (page_to_pfn(STbp->sg[i].page) == page_to_pfn(STbp->sg[i-1].page) + 1)
STp->nbr_combinable++;
}
)
} else
STbp->do_dio = FALSE;
DEB( STp->nbr_requests++; )
if (!STbp->do_dio) {
if (STp->block_size)
bufsize = STp->block_size > st_fixed_buffer_size ?
STp->block_size : st_fixed_buffer_size;
else
bufsize = count;
if (bufsize > STbp->buffer_size &&
!enlarge_buffer(STbp, bufsize, STp->restr_dma)) {
retval = (-EOVERFLOW);
goto out;
}
if (STp->block_size)
STbp->buffer_blocks = bufsize / STp->block_size;
}
out:
return retval;
}
/* Write command */
static ssize_t
st_write(struct file *filp, const char *buf, size_t count, loff_t * ppos)
{
ssize_t total;
ssize_t i, do_count, blks, transfer;
ssize_t retval;
int undone, retry_eot = 0, scode;
int async_write;
unsigned char cmd[MAX_COMMAND_SIZE];
const char *b_point;
Scsi_Request *SRpnt = NULL;
Scsi_Tape *STp = filp->private_data;
ST_mode *STm;
ST_partstat *STps;
ST_buffer *STbp;
char *name = tape_name(STp);
if (down_interruptible(&STp->lock))
return -ERESTARTSYS;
retval = rw_checks(STp, filp, count, ppos);
if (retval || count == 0)
goto out;
/* Write must be integral number of blocks */
if (STp->block_size != 0 && (count % STp->block_size) != 0) {
printk(KERN_WARNING "%s: Write not multiple of tape block size.\n",
name);
retval = (-EINVAL);
goto out;
}
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
if (STp->write_prot) {
retval = (-EACCES);
goto out;
}
if (STps->rw == ST_READING) {
retval = flush_buffer(STp, 0);
if (retval)
goto out;
STps->rw = ST_WRITING;
} else if (STps->rw != ST_WRITING &&
STps->drv_file == 0 && STps->drv_block == 0) {
if ((retval = set_mode_densblk(STp, STm)) < 0)
goto out;
if (STm->default_compression != ST_DONT_TOUCH &&
!(STp->compression_changed)) {
if (st_compression(STp, (STm->default_compression == ST_YES))) {
printk(KERN_WARNING "%s: Can't set default compression.\n",
name);
if (modes_defined) {
retval = (-EINVAL);
goto out;
}
}
}
}
STbp = STp->buffer;
if (STbp->writing) {
write_behind_check(STp);
if (STbp->syscall_result) {
DEBC(printk(ST_DEB_MSG "%s: Async write error (write) %x.\n",
name, STbp->midlevel_result));
if (STbp->midlevel_result == INT_MAX)
STps->eof = ST_EOM_OK;
else
STps->eof = ST_EOM_ERROR;
}
}
if (STps->eof == ST_EOM_OK) {
STps->eof = ST_EOD_1; /* allow next write */
retval = (-ENOSPC);
goto out;
}
else if (STps->eof == ST_EOM_ERROR) {
retval = (-EIO);
goto out;
}
/* Check the buffer readability in cases where copy_user might catch
the problems after some tape movement. */
if (STp->block_size != 0 &&
!STbp->do_dio &&
(copy_from_user(&i, buf, 1) != 0 ||
copy_from_user(&i, buf + count - 1, 1) != 0)) {
retval = (-EFAULT);
goto out;
}
retval = setup_buffering(STp, buf, count, FALSE);
if (retval)
goto out;
total = count;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = WRITE_6;
cmd[1] = (STp->block_size != 0);
STps->rw = ST_WRITING;
b_point = buf;
while (count > 0 && !retry_eot) {
if (STbp->do_dio) {
do_count = count;
}
else {
if (STp->block_size == 0)
do_count = count;
else {
do_count = STbp->buffer_blocks * STp->block_size -
STbp->buffer_bytes;
if (do_count > count)
do_count = count;
}
i = append_to_buffer(b_point, STbp, do_count);
if (i) {
retval = i;
goto out;
}
}
count -= do_count;
filp->f_pos += do_count;
b_point += do_count;
async_write = STp->block_size == 0 && !STbp->do_dio &&
STm->do_async_writes && STps->eof < ST_EOM_OK;
if (STp->block_size != 0 && STm->do_buffer_writes &&
!(STp->try_dio && try_wdio) && STps->eof < ST_EOM_OK &&
STbp->buffer_bytes < STbp->buffer_size) {
STp->dirty = TRUE;
/* Don't write a buffer that is not full enough. */
if (!async_write && count == 0)
break;
}
retry_write:
if (STp->block_size == 0)
blks = transfer = do_count;
else {
if (!STbp->do_dio)
blks = STbp->buffer_bytes;
else
blks = do_count;
blks /= STp->block_size;
transfer = blks * STp->block_size;
}
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, SCSI_DATA_WRITE,
STp->timeout, MAX_WRITE_RETRIES, !async_write);
if (!SRpnt) {
retval = STbp->syscall_result;
goto out;
}
if (async_write) {
STbp->writing = transfer;
STp->dirty = !(STbp->writing ==
STbp->buffer_bytes);
SRpnt = NULL; /* Prevent releasing this request! */
DEB( STp->write_pending = 1; )
break;
}
if (STbp->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "%s: Error on write:\n", name));
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
(SRpnt->sr_sense_buffer[2] & 0x40)) {
scode = SRpnt->sr_sense_buffer[2] & 0x0f;
if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0)
undone = (SRpnt->sr_sense_buffer[3] << 24) |
(SRpnt->sr_sense_buffer[4] << 16) |
(SRpnt->sr_sense_buffer[5] << 8) |
SRpnt->sr_sense_buffer[6];
else if (STp->block_size == 0 &&
scode == VOLUME_OVERFLOW)
undone = transfer;
else
undone = 0;
if (STp->block_size != 0)
undone *= STp->block_size;
filp->f_pos -= undone;
if (undone <= do_count) {
/* Only data from this write is not written */
count += undone;
do_count -= undone;
if (STp->block_size)
blks = (transfer - undone) / STp->block_size;
STps->eof = ST_EOM_OK;
/* Continue in fixed block mode if all written
in this request but still something left to write
(retval left to zero)
*/
if (STp->block_size == 0 ||
undone > 0 || count == 0)
retval = (-ENOSPC); /* EOM within current request */
DEBC(printk(ST_DEB_MSG
"%s: EOM with %d bytes unwritten.\n",
name, count));
} else {
/* EOT within data buffered earlier (possible only
in fixed block mode without direct i/o) */
if (!retry_eot && (SRpnt->sr_sense_buffer[0] & 1) == 0 &&
(scode == NO_SENSE || scode == RECOVERED_ERROR)) {
move_buffer_data(STp->buffer, transfer - undone);
retry_eot = TRUE;
if (STps->drv_block >= 0) {
STps->drv_block += (transfer - undone) /
STp->block_size;
}
STps->eof = ST_EOM_OK;
DEBC(printk(ST_DEB_MSG
"%s: Retry write of %d bytes at EOM.\n",
name, STp->buffer->buffer_bytes));
goto retry_write;
}
else {
/* Either error within data buffered by driver or
failed retry */
count -= do_count;
blks = do_count = 0;
STps->eof = ST_EOM_ERROR;
STps->drv_block = (-1); /* Too cautious? */
retval = (-EIO); /* EOM for old data */
DEBC(printk(ST_DEB_MSG
"%s: EOM with lost data.\n",
name));
}
}
} else {
filp->f_pos -= do_count;
STps->drv_block = (-1); /* Too cautious? */
retval = (-EIO);
}
}
if (STps->drv_block >= 0) {
if (STp->block_size == 0)
STps->drv_block += (do_count > 0);
else
STps->drv_block += blks;
}
STbp->buffer_bytes = 0;
STp->dirty = 0;
if (retval || retry_eot) {
if (count < total)
retval = total - count;
goto out;
}
}
if (STps->eof == ST_EOD_1)
STps->eof = ST_EOM_OK;
else if (STps->eof != ST_EOM_OK)
STps->eof = ST_NOEOF;
retval = total - count;
out:
if (SRpnt != NULL)
scsi_release_request(SRpnt);
STbp = STp->buffer;
if (STbp->do_dio) {
sgl_unmap_user_pages(&(STbp->sg[0]), STbp->do_dio, FALSE);
STbp->do_dio = 0;
}
up(&STp->lock);
return retval;
}
/* 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(Scsi_Tape *STp, long count, Scsi_Request ** aSRpnt)
{
int transfer, blks, bytes;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
ST_mode *STm;
ST_partstat *STps;
ST_buffer *STbp;
int retval = 0;
char *name = tape_name(STp);
if (count == 0)
return 0;
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
if (STps->eof == ST_FM_HIT)
return 1;
STbp = STp->buffer;
if (STp->block_size == 0)
blks = bytes = count;
else {
if (!(STp->try_dio && try_rdio) && STm->do_read_ahead) {
blks = (STp->buffer)->buffer_blocks;
bytes = blks * STp->block_size;
} else {
bytes = count;
if (!STbp->do_dio && bytes > (STp->buffer)->buffer_size)
bytes = (STp->buffer)->buffer_size;
blks = bytes / STp->block_size;
bytes = blks * STp->block_size;
}
}
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = READ_6;
cmd[1] = (STp->block_size != 0);
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
SRpnt = *aSRpnt;
SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, SCSI_DATA_READ,
STp->timeout, MAX_RETRIES, TRUE);
*aSRpnt = SRpnt;
if (!SRpnt)
return STbp->syscall_result;
STbp->read_pointer = 0;
STps->at_sm = 0;
/* Something to check */
if (STbp->syscall_result) {
retval = 1;
DEBC(printk(ST_DEB_MSG "%s: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
name,
SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[1],
SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3],
SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5],
SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7]));
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK)
SRpnt->sr_sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
if ((SRpnt->sr_sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
/* Compute the residual count */
if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0)
transfer = (SRpnt->sr_sense_buffer[3] << 24) |
(SRpnt->sr_sense_buffer[4] << 16) |
(SRpnt->sr_sense_buffer[5] << 8) |
SRpnt->sr_sense_buffer[6];
else
transfer = 0;
if (STp->block_size == 0 &&
(SRpnt->sr_sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
transfer = bytes;
if (SRpnt->sr_sense_buffer[2] & 0x20) { /* ILI */
if (STp->block_size == 0) {
if (transfer <= 0) {
if (transfer < 0)
printk(KERN_NOTICE
"%s: Failed to read %d byte block with %d byte transfer.\n",
name, bytes - transfer, bytes);
if (STps->drv_block >= 0)
STps->drv_block += 1;
STbp->buffer_bytes = 0;
return (-ENOMEM);
}
STbp->buffer_bytes = bytes - transfer;
} else {
scsi_release_request(SRpnt);
SRpnt = *aSRpnt = NULL;
if (transfer == blks) { /* We did not get anything, error */
printk(KERN_NOTICE "%s: Incorrect block size.\n", name);
if (STps->drv_block >= 0)
STps->drv_block += blks - transfer + 1;
st_int_ioctl(STp, MTBSR, 1);
return (-EIO);
}
/* We have some data, deliver it */
STbp->buffer_bytes = (blks - transfer) *
STp->block_size;
DEBC(printk(ST_DEB_MSG
"%s: ILI but enough data received %ld %d.\n",
name, count, STbp->buffer_bytes));
if (STps->drv_block >= 0)
STps->drv_block += 1;
if (st_int_ioctl(STp, MTBSR, 1))
return (-EIO);
}
} else if (SRpnt->sr_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)
STbp->buffer_bytes = 0;
else
STbp->buffer_bytes =
bytes - transfer * STp->block_size;
DEBC(printk(ST_DEB_MSG
"%s: EOF detected (%d bytes read).\n",
name, STbp->buffer_bytes));
} else if (SRpnt->sr_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)
STbp->buffer_bytes = bytes - transfer;
else
STbp->buffer_bytes =
bytes - transfer * STp->block_size;
DEBC(printk(ST_DEB_MSG "%s: EOM detected (%d bytes read).\n",
name, STbp->buffer_bytes));
}
}
/* end of EOF, EOM, ILI test */
else { /* nonzero sense key */
DEBC(printk(ST_DEB_MSG
"%s: Tape error while reading.\n", name));
STps->drv_block = (-1);
if (STps->eof == ST_FM &&
(SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) {
DEBC(printk(ST_DEB_MSG
"%s: Zero returned for first BLANK CHECK after EOF.\n",
name));
STps->eof = ST_EOD_2; /* First BLANK_CHECK after FM */
} else /* Some other extended sense code */
retval = (-EIO);
}
if (STbp->buffer_bytes < 0) /* Caused by bogus sense data */
STbp->buffer_bytes = 0;
}
/* End of extended sense test */
else { /* Non-extended sense */
retval = STbp->syscall_result;
}
}
/* End of error handling */
else /* Read successful */
STbp->buffer_bytes = bytes;
if (STps->drv_block >= 0) {
if (STp->block_size == 0)
STps->drv_block++;
else
STps->drv_block += STbp->buffer_bytes / STp->block_size;
}
return retval;
}
/* Read command */
static ssize_t
st_read(struct file *filp, char *buf, size_t count, loff_t * ppos)
{
ssize_t total;
ssize_t retval = 0;
ssize_t i, transfer;
int special;
Scsi_Request *SRpnt = NULL;
Scsi_Tape *STp = filp->private_data;
ST_mode *STm;
ST_partstat *STps;
ST_buffer *STbp = STp->buffer;
DEB( char *name = tape_name(STp); )
if (down_interruptible(&STp->lock))
return -ERESTARTSYS;
retval = rw_checks(STp, filp, count, ppos);
if (retval || count == 0)
goto out;
STm = &(STp->modes[STp->current_mode]);
if (!(STm->do_read_ahead) && STp->block_size != 0 &&
(count % STp->block_size) != 0) {
retval = (-EINVAL); /* Read must be integral number of blocks */
goto out;
}
STps = &(STp->ps[STp->partition]);
if (STps->rw == ST_WRITING) {
retval = flush_buffer(STp, 0);
if (retval)
goto out;
STps->rw = ST_READING;
}
DEB(
if (debugging && STps->eof != ST_NOEOF)
printk(ST_DEB_MSG "%s: EOF/EOM flag up (%d). Bytes %d\n", name,
STps->eof, STbp->buffer_bytes);
) /* end DEB */
retval = setup_buffering(STp, buf, count, TRUE);
if (retval)
goto out;
if (STbp->buffer_bytes == 0 &&
STps->eof >= ST_EOD_1) {
if (STps->eof < ST_EOD) {
STps->eof += 1;
retval = 0;
goto out;
}
retval = (-EIO); /* EOM or Blank Check */
goto out;
}
if (!STbp->do_dio) {
/* 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) {
retval = (-EFAULT);
goto out;
}
}
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 (STbp->buffer_bytes == 0) {
special = read_tape(STp, count - total, &SRpnt);
if (special < 0) { /* No need to continue read */
retval = special;
goto out;
}
}
/* Move the data from driver buffer to user buffer */
if (STbp->buffer_bytes > 0) {
DEB(
if (debugging && STps->eof != ST_NOEOF)
printk(ST_DEB_MSG
"%s: EOF up (%d). Left %d, needed %d.\n", name,
STps->eof, STbp->buffer_bytes,
count - total);
) /* end DEB */
transfer = STbp->buffer_bytes < count - total ?
STbp->buffer_bytes : count - total;
if (!STbp->do_dio) {
i = from_buffer(STbp, buf, transfer);
if (i) {
retval = i;
goto out;
}
}
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; ) */
/* 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;
retval = total;
out:
if (SRpnt != NULL) {
scsi_release_request(SRpnt);
SRpnt = NULL;
}
if (STbp->do_dio) {
sgl_unmap_user_pages(&(STbp->sg[0]), STbp->do_dio, TRUE);
STbp->do_dio = 0;
STbp->buffer_bytes = 0;
}
up(&STp->lock);
return retval;
}
/* Set the driver options */
static void st_log_options(Scsi_Tape * STp, ST_mode * STm, char *name)
{
printk(KERN_INFO
"%s: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
name, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
STm->do_read_ahead);
printk(KERN_INFO
"%s: can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
name, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
printk(KERN_INFO
"%s: defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
STp->scsi2_logical);
printk(KERN_INFO
"%s: sysv: %d nowait: %d\n", name, STm->sysv, STp->immediate);
DEB(printk(KERN_INFO
"%s: debugging: %d\n",
name, debugging);)
}
static int st_set_options(Scsi_Tape *STp, long options)
{
int value;
long code;
ST_mode *STm;
char *name = tape_name(STp);
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined) {
memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
modes_defined = TRUE;
DEBC(printk(ST_DEB_MSG
"%s: Initialized mode %d definition from mode 0\n",
name, STp->current_mode));
}
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;
STp->immediate = (options & MT_ST_NOWAIT) != 0;
STm->sysv = (options & MT_ST_SYSV) != 0;
DEB( debugging = (options & MT_ST_DEBUGGING) != 0; )
st_log_options(STp, STm, name);
} 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_NOWAIT) != 0)
STp->immediate = value;
if ((options & MT_ST_SYSV) != 0)
STm->sysv = value;
DEB(
if ((options & MT_ST_DEBUGGING) != 0)
debugging = value; )
st_log_options(STp, STm, name);
} else if (code == MT_ST_WRITE_THRESHOLD) {
/* Retained for compatibility */
} else if (code == MT_ST_DEF_BLKSIZE) {
value = (options & ~MT_ST_OPTIONS);
if (value == ~MT_ST_OPTIONS) {
STm->default_blksize = (-1);
printk(KERN_INFO "%s: Default block size disabled.\n", name);
} else {
STm->default_blksize = value;
printk(KERN_INFO "%s: Default block size set to %d bytes.\n",
name, STm->default_blksize);
if (STp->ready == ST_READY) {
STp->blksize_changed = FALSE;
set_mode_densblk(STp, STm);
}
}
} 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 "%s: Long timeout set to %d seconds.\n", name,
(value & ~MT_ST_SET_LONG_TIMEOUT));
} else {
STp->timeout = value * HZ;
printk(KERN_INFO "%s: Normal timeout set to %d seconds.\n",
name, value);
}
} else if (code == MT_ST_SET_CLN) {
value = (options & ~MT_ST_OPTIONS) & 0xff;
if (value != 0 &&
value < EXTENDED_SENSE_START && value >= SCSI_SENSE_BUFFERSIZE)
return (-EINVAL);
STp->cln_mode = value;
STp->cln_sense_mask = (options >> 8) & 0xff;
STp->cln_sense_value = (options >> 16) & 0xff;
printk(KERN_INFO
"%s: Cleaning request mode %d, mask %02x, value %02x\n",
name, value, STp->cln_sense_mask, STp->cln_sense_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 "%s: Density default disabled.\n",
name);
} else {
STm->default_density = value & 0xff;
printk(KERN_INFO "%s: Density default set to %x\n",
name, STm->default_density);
if (STp->ready == ST_READY) {
STp->density_changed = FALSE;
set_mode_densblk(STp, STm);
}
}
} else if (code == MT_ST_DEF_DRVBUFFER) {
if (value == MT_ST_CLEAR_DEFAULT) {
STp->default_drvbuffer = 0xff;
printk(KERN_INFO
"%s: Drive buffer default disabled.\n", name);
} else {
STp->default_drvbuffer = value & 7;
printk(KERN_INFO
"%s: Drive buffer default set to %x\n",
name, STp->default_drvbuffer);
if (STp->ready == ST_READY)
st_int_ioctl(STp, MTSETDRVBUFFER, 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
"%s: Compression default disabled.\n", name);
} else {
if ((value & 0xff00) != 0) {
STp->c_algo = (value & 0xff00) >> 8;
printk(KERN_INFO "%s: Compression algorithm set to 0x%x.\n",
name, STp->c_algo);
}
if ((value & 0xff) != 0xff) {
STm->default_compression = (value & 1 ? ST_YES : ST_NO);
printk(KERN_INFO "%s: Compression default set to %x\n",
name, (value & 1));
if (STp->ready == ST_READY) {
STp->compression_changed = FALSE;
st_compression(STp, (STm->default_compression == ST_YES));
}
}
}
}
} else
return (-EIO);
return 0;
}
#define MODE_HEADER_LENGTH 4
/* Mode header and page byte offsets */
#define MH_OFF_DATA_LENGTH 0
#define MH_OFF_MEDIUM_TYPE 1
#define MH_OFF_DEV_SPECIFIC 2
#define MH_OFF_BDESCS_LENGTH 3
#define MP_OFF_PAGE_NBR 0
#define MP_OFF_PAGE_LENGTH 1
/* Mode header and page bit masks */
#define MH_BIT_WP 0x80
#define MP_MSK_PAGE_NBR 0x3f
/* Don't return block descriptors */
#define MODE_SENSE_OMIT_BDESCS 0x08
#define MODE_SELECT_PAGE_FORMAT 0x10
/* Read a mode page into the tape buffer. The block descriptors are included
if incl_block_descs is true. The page control is ored to the page number
parameter, if necessary. */
static int read_mode_page(Scsi_Tape *STp, int page, int omit_block_descs)
{
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt = NULL;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SENSE;
if (omit_block_descs)
cmd[1] = MODE_SENSE_OMIT_BDESCS;
cmd[2] = page;
cmd[4] = 255;
SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ,
STp->timeout, 0, TRUE);
if (SRpnt == NULL)
return (STp->buffer)->syscall_result;
scsi_release_request(SRpnt);
return (STp->buffer)->syscall_result;
}
/* Send the mode page in the tape buffer to the drive. Assumes that the mode data
in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */
static int write_mode_page(Scsi_Tape *STp, int page, int slow)
{
int pgo;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt = NULL;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SELECT;
cmd[1] = MODE_SELECT_PAGE_FORMAT;
pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH];
cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2;
/* Clear reserved fields */
(STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0;
(STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0;
(STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
(STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
(slow ? STp->long_timeout : STp->timeout), 0, TRUE);
if (SRpnt == NULL)
return (STp->buffer)->syscall_result;
scsi_release_request(SRpnt);
return (STp->buffer)->syscall_result;
}
#define COMPRESSION_PAGE 0x0f
#define COMPRESSION_PAGE_LENGTH 16
#define CP_OFF_DCE_DCC 2
#define CP_OFF_C_ALGO 7
#define DCE_MASK 0x80
#define DCC_MASK 0x40
#define RED_MASK 0x60
/* Control the compression with mode page 15. Algorithm not changed if zero.
The block descriptors are read and written because Sony SDT-7000 does not
work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
Including block descriptors should not cause any harm to other drives. */
static int st_compression(Scsi_Tape * STp, int state)
{
int retval;
int mpoffs; /* Offset to mode page start */
unsigned char *b_data = (STp->buffer)->b_data;
DEB( char *name = tape_name(STp); )
if (STp->ready != ST_READY)
return (-EIO);
/* Read the current page contents */
retval = read_mode_page(STp, COMPRESSION_PAGE, FALSE);
if (retval) {
DEBC(printk(ST_DEB_MSG "%s: Compression mode page not supported.\n",
name));
return (-EIO);
}
mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
DEBC(printk(ST_DEB_MSG "%s: Compression state is %d.\n", name,
(b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0)));
/* Check if compression can be changed */
if ((b_data[mpoffs + CP_OFF_DCE_DCC] & DCC_MASK) == 0) {
DEBC(printk(ST_DEB_MSG "%s: Compression not supported.\n", name));
return (-EIO);
}
/* Do the change */
if (state) {
b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
if (STp->c_algo != 0)
b_data[mpoffs + CP_OFF_C_ALGO] = STp->c_algo;
}
else {
b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
if (STp->c_algo != 0)
b_data[mpoffs + CP_OFF_C_ALGO] = 0; /* no compression */
}
retval = write_mode_page(STp, COMPRESSION_PAGE, FALSE);
if (retval) {
DEBC(printk(ST_DEB_MSG "%s: Compression change failed.\n", name));
return (-EIO);
}
DEBC(printk(ST_DEB_MSG "%s: Compression state changed to %d.\n",
name, state));
STp->compression_changed = TRUE;
return 0;
}
/* Process the load and unload commands (does unload if the load code is zero) */
static int do_load_unload(Scsi_Tape *STp, struct file *filp, int load_code)
{
int retval = (-EIO), timeout;
DEB( char *name = tape_name(STp); )
unsigned char cmd[MAX_COMMAND_SIZE];
ST_partstat *STps;
Scsi_Request *SRpnt;
if (STp->ready != ST_READY && !load_code) {
if (STp->ready == ST_NO_TAPE)
return (-ENOMEDIUM);
else
return (-EIO);
}
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = START_STOP;
if (load_code)
cmd[4] |= 1;
/*
* If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A
*/
if (load_code >= 1 + MT_ST_HPLOADER_OFFSET
&& load_code <= 6 + MT_ST_HPLOADER_OFFSET) {
DEBC(printk(ST_DEB_MSG "%s: Enhanced %sload slot %2d.\n",
name, (cmd[4]) ? "" : "un",
load_code - MT_ST_HPLOADER_OFFSET));
cmd[3] = load_code - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
}
if (STp->immediate) {
cmd[1] = 1; /* Don't wait for completion */
timeout = STp->timeout;
}
else
timeout = STp->long_timeout;
DEBC(
if (!load_code)
printk(ST_DEB_MSG "%s: Unloading tape.\n", name);
else
printk(ST_DEB_MSG "%s: Loading tape.\n", name);
);
SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE,
timeout, MAX_RETRIES, TRUE);
if (!SRpnt)
return (STp->buffer)->syscall_result;
retval = (STp->buffer)->syscall_result;
scsi_release_request(SRpnt);
if (!retval) { /* SCSI command successful */
if (!load_code) {
STp->rew_at_close = 0;
STp->ready = ST_NO_TAPE;
}
else {
STp->rew_at_close = STp->autorew_dev;
retval = check_tape(STp, filp);
if (retval > 0)
retval = 0;
}
}
else {
STps = &(STp->ps[STp->partition]);
STps->drv_file = STps->drv_block = (-1);
}
return retval;
}
/* Internal ioctl function */
static int st_int_ioctl(Scsi_Tape *STp, unsigned int cmd_in, unsigned long arg)
{
int timeout;
long ltmp;
int ioctl_result;
int chg_eof = TRUE;
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request *SRpnt;
ST_partstat *STps;
int fileno, blkno, at_sm, undone;
int datalen = 0, direction = SCSI_DATA_NONE;
char *name = tape_name(STp);
if (STp->ready != ST_READY) {
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, MAX_COMMAND_SIZE);
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;
DEBC(printk(ST_DEB_MSG "%s: Spacing tape forward over %d filemarks.\n",
name, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]));
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;
DEBC(
if (cmd[2] & 0x80)
ltmp = 0xff000000;
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk(ST_DEB_MSG
"%s: Spacing tape backward over %ld filemarks.\n",
name, (-ltmp));
)
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;
DEBC(printk(ST_DEB_MSG "%s: Spacing tape forward %d blocks.\n", name,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]));
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;
DEBC(
if (cmd[2] & 0x80)
ltmp = 0xff000000;
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk(ST_DEB_MSG
"%s: Spacing tape backward %ld blocks.\n", name, (-ltmp));
)
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;
DEBC(printk(ST_DEB_MSG "%s: Spacing tape forward %d setmarks.\n", name,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]));
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;
DEBC(
if (cmd[2] & 0x80)
ltmp = 0xff000000;
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk(ST_DEB_MSG "%s: Spacing tape backward %ld setmarks.\n",
name, (-ltmp));
)
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;
DEBC(
if (cmd_in == MTWEOF)
printk(ST_DEB_MSG "%s: Writing %d filemarks.\n", name,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);