/* The driver prints some debugging information on the console if DEBUG
is defined and non-zero. */ #define DEBUG 1 #define NO_DEBUG 0
#define ST_DEB_MSG KERN_NOTICE #if DEBUG /* The message level for the debug messages is currently set to KERN_NOTICE sothatpeoplecaneasilyseethemessages.Laterwhenthedebuggingmessages
in the drivers are more widely classified, this may be changed to KERN_DEBUG. */ #define DEB(a) a #define DEBC(a) if (debugging) { a ; } #else #define DEB(a) #define DEBC(a) #endif
/* Set 'perm' (4th argument) to 0 to disable module_param's definition *ofsysfsparameters(whichmodule_paramdoesn'tyetsupport). *Sysfsparametersdefinedexplicitlylater.
*/
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)");
module_param_named(debug_flag, debug_flag, int, 0);
MODULE_PARM_DESC(debug_flag, "Enable DEBUG, same as setting debugging=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");
/* Restrict the number of modes so that names for all are assigned */ #if ST_NBR_MODES > 16 #error"Maximum number of modes is 16" #endif /* Bit reversed order to get same names for same minors with all
mode counts */ staticconstchar *st_formats[] = { "", "r", "k", "s", "l", "t", "o", "u", "m", "v", "p", "x", "a", "y", "q", "z"};
/* The default definitions have been moved to st_options.h */
/* 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
staticint debugging = DEBUG;
/* Setting these non-zero may risk recognizing resets */ #define MAX_RETRIES 0 #define MAX_WRITE_RETRIES 0 #define MAX_READY_RETRIES 0
/* 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) */ staticchar * st_incompatible(struct 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;
}
/* Do the scsi command. Waits until command performed if do_wait is true. Otherwisewrite_behind_check()isusedtocheckthatthecommand
has finished. */ staticstruct st_request *
st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsignedchar *cmd, int bytes, int direction, int timeout, int retries, int do_wait)
{ struct completion *waiting; struct rq_map_data *mdata = &STp->buffer->map_data; int ret;
/* if async, make sure there's no command outstanding */ if (!do_wait && ((STp->buffer)->last_SRpnt)) {
st_printk(KERN_ERR, STp, "Async command already active.\n"); if (signal_pending(current))
(STp->buffer)->syscall_result = (-EINTR); else
(STp->buffer)->syscall_result = (-EBUSY); return NULL;
}
if (!SRpnt) {
SRpnt = st_allocate_request(STp); if (!SRpnt) return NULL;
}
/* If async IO, set last_SRpnt. This ptr tells write_behind_check
which IO is outstanding. It's nulled out when the IO completes. */ if (!do_wait)
(STp->buffer)->last_SRpnt = SRpnt;
ret = st_scsi_execute(SRpnt, cmd, direction, NULL, bytes, timeout,
retries); if (ret) { /* could not allocate the buffer or request was too large */
(STp->buffer)->syscall_result = (-EBUSY);
(STp->buffer)->last_SRpnt = NULL;
} elseif (do_wait) {
wait_for_completion(waiting);
SRpnt->waiting = NULL;
(STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
}
return SRpnt;
}
/* Handle the write-behind checking (waits for completion). Returns -ENOSPC if writehasbeencorrectbutEOMearlywarningreached,-EIOifwriteendedin errororzeroifwritesuccessful.Asynchronouswritesareusedonlyin
variable block mode. */ staticint write_behind_check(struct scsi_tape * STp)
{ int retval = 0; struct st_buffer *STbuffer; struct st_partstat *STps; struct st_cmdstatus *cmdstatp; struct st_request *SRpnt;
STbuffer = STp->buffer; if (!STbuffer->writing) return0;
DEB( if (STp->write_pending)
STp->nbr_waits++; else
STp->nbr_finished++;
) /* end DEB */
cmdstatp = &STbuffer->cmdstat; if (STbuffer->syscall_result) {
retval = -EIO; if (cmdstatp->have_sense && !cmdstatp->deferred &&
(cmdstatp->flags & SENSE_EOM) &&
(cmdstatp->sense_hdr.sense_key == NO_SENSE ||
cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR)) { /* EOM at write-behind, has all data been written? */ if (!cmdstatp->remainder_valid ||
cmdstatp->uremainder64 == 0)
retval = -ENOSPC;
} if (retval == -EIO)
STps->drv_block = -1;
}
STbuffer->writing = 0;
DEB(if (debugging && retval)
st_printk(ST_DEB_MSG, STp, "Async write error %x, return value %d.\n",
STbuffer->cmdstat.midlevel_result, retval);) /* end DEB */
return retval;
}
/* Step over EOF if it has been inadvertently crossed (ioctl not used because
it messes up the block number). */ staticint cross_eof(struct scsi_tape * STp, int forward)
{ struct st_request *SRpnt; unsignedchar cmd[MAX_COMMAND_SIZE];
if (cmdstatp->have_sense && !cmdstatp->deferred &&
(cmdstatp->flags & SENSE_EOM) &&
(cmdstatp->sense_hdr.sense_key == NO_SENSE ||
cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
(!cmdstatp->remainder_valid ||
cmdstatp->uremainder64 == 0)) { /* All written at EOM early warning */
STp->dirty = 0;
(STp->buffer)->buffer_bytes = 0; if (STps->drv_block >= 0)
STps->drv_block += blks;
result = (-ENOSPC);
} else {
st_printk(KERN_ERR, STp, "Error on flush.\n");
STps->drv_block = (-1);
result = (-EIO);
}
} else { if (STps->drv_block >= 0)
STps->drv_block += blks;
STp->dirty = 0;
(STp->buffer)->buffer_bytes = 0;
}
st_release_request(SRpnt);
SRpnt = NULL;
} return result;
}
/* Flush the tape buffer. The tape will be positioned correctly unless
seek_next is true. */ staticint flush_buffer(struct scsi_tape *STp, int seek_next)
{ int backspace, result; struct st_partstat *STps;
if (STp->ready != ST_READY) return0;
/* *Iftherewasabusreset,blockfurtheraccess *tothisdevice.
*/ if (STp->pos_unknown) return (-EIO);
/* Set the internal state after reset */ staticvoid reset_state(struct scsi_tape *STp)
{ int i; struct 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 = 0;
STps->drv_block = -1;
STps->drv_file = -1;
} if (STp->can_partitions) {
STp->partition = find_partition(STp); if (STp->partition < 0)
STp->partition = 0;
}
}
/* 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
if (STp->can_partitions && STp->nbr_partitions < 1) { /* This code is reached when the device is opened for the first time afterthedriverhasbeeninitializedwithtapeinthedriveandthe
partition support has been enabled. */
DEBC_printk(STp, "Updating partition number in status.\n"); 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 = 0;
STp->compression_changed = 0; 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))
st_printk(KERN_WARNING, STp, "Can't set default drive " "buffering to %d.\n",
STp->default_drvbuffer);
}
}
return CHKRES_READY;
err_out: return retval;
}
/* Open the device. Needs to take the BKL only because of incrementing the SCSI host
module count. */ staticint st_open(struct inode *inode, struct file *filp)
{ int i, retval = (-EIO); int resumed = 0; struct scsi_tape *STp; struct st_partstat *STps; int dev = TAPE_NR(inode);
/* See that we have at least a one page buffer available */ if (!enlarge_buffer(STp->buffer, PAGE_SIZE)) {
st_printk(KERN_WARNING, STp, "Can't allocate one page tape buffer.\n");
retval = (-EOVERFLOW); goto err_out;
}
/* Flush the tape buffer before close */ staticint st_flush(struct file *filp, fl_owner_t id)
{ int result = 0, result2; unsignedchar cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt; struct scsi_tape *STp = filp->private_data; struct st_modedef *STm = &(STp->modes[STp->current_mode]); struct st_partstat *STps = &(STp->ps[STp->partition]);
if (file_count(filp) > 1) return0;
if (STps->rw == ST_WRITING && !STp->pos_unknown) {
result = st_flush_write_buffer(STp); if (result != 0 && result != (-ENOSPC)) goto out;
}
if (STp->can_partitions &&
(result2 = switch_partition(STp)) < 0) {
DEBC_printk(STp, "switch_partition at close failed.\n"); if (result == 0)
result = result2; goto out;
}
DEBC( if (STp->nbr_requests)
st_printk(KERN_DEBUG, STp, "Number of r/w requests %d, dio used in %d, " "pages %d.\n", STp->nbr_requests, STp->nbr_dio,
STp->nbr_pages));
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. */ staticint st_release(struct inode *inode, struct file *filp)
{ struct scsi_tape *STp = filp->private_data;
if (STp->door_locked == ST_LOCKED_AUTO)
do_door_lock(STp, 0);
/* The checks common to both reading and writing */ static ssize_t rw_checks(struct scsi_tape *STp, struct file *filp, size_t count)
{
ssize_t retval = 0;
staticint setup_buffering(struct scsi_tape *STp, constchar __user *buf,
size_t count, int is_read)
{ int i, bufsize, retval = 0; struct st_buffer *STbp = STp->buffer;
if (is_read)
i = STp->try_dio_now && try_rdio; else
i = STp->try_dio_now && try_wdio;
if (i && ((unsignedlong)buf & queue_dma_alignment(
STp->device->request_queue)) == 0) {
i = sgl_map_user_pages(STbp, STbp->use_sg, (unsignedlong)buf,
count, (is_read ? READ : WRITE)); if (i > 0) {
STbp->do_dio = i;
STbp->buffer_bytes = 0; /* can be used as transfer counter */
} else
STbp->do_dio = 0; /* fall back to buffering with any error */
STbp->sg_segs = STbp->do_dio;
DEB( if (STbp->do_dio) {
STp->nbr_dio++;
STp->nbr_pages += STbp->do_dio;
}
)
} else
STbp->do_dio = 0;
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; /* Make sure that data from previous user is not leaked even if
HBA does not return correct residual */ if (is_read && STp->sili && !STbp->cleared)
clear_buffer(STbp);
}
/* Can be called more than once after each setup_buffer() */ staticvoid release_buffering(struct scsi_tape *STp, int is_read)
{ struct st_buffer *STbp;
/* Write must be integral number of blocks */ if (STp->block_size != 0 && (count % STp->block_size) != 0) {
st_printk(KERN_WARNING, STp, "Write not multiple of tape block size.\n");
retval = (-EINVAL); goto out;
}
if (STbp->syscall_result != 0) { struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
DEBC_printk(STp, "Error on write:\n"); if (cmdstatp->have_sense && (cmdstatp->flags & SENSE_EOM)) {
scode = cmdstatp->sense_hdr.sense_key; if (cmdstatp->remainder_valid)
undone = (int)cmdstatp->uremainder64; elseif (STp->block_size == 0 &&
scode == VOLUME_OVERFLOW)
undone = transfer; else
undone = 0; if (STp->block_size != 0)
undone *= STp->block_size; if (undone <= do_count) { /* Only data from this write is not written */
count += undone;
b_point -= 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 inthisrequestbutstillsomethinglefttowrite (retvallefttozero)
*/ if (STp->block_size == 0 ||
undone > 0 || count == 0)
retval = (-ENOSPC); /* EOM within current request */
DEBC_printk(STp, "EOM with %d " "bytes unwritten.\n",
(int)count);
} else { /* EOT within data buffered earlier (possible only
in fixed block mode without direct i/o) */ if (!retry_eot && !cmdstatp->deferred &&
(scode == NO_SENSE || scode == RECOVERED_ERROR)) {
move_buffer_data(STp->buffer, transfer - undone);
retry_eot = 1; if (STps->drv_block >= 0) {
STps->drv_block += (transfer - undone) /
STp->block_size;
}
STps->eof = ST_EOM_OK;
DEBC_printk(STp, "Retry " "write of %d " "bytes at EOM.\n",
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(STp, "EOM with " "lost data.\n");
}
}
} else {
count += do_count;
STps->drv_block = (-1); /* Too cautious? */
retval = STbp->syscall_result;
}
}
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; elseif (STps->eof != ST_EOM_OK)
STps->eof = ST_NOEOF;
retval = total - count;
out: if (SRpnt != NULL)
st_release_request(SRpnt);
release_buffering(STp, 0);
mutex_unlock(&STp->lock);
return retval;
}
/* Read data from the tape. Returns zero in the normal case, one if the eofstatushaschanged,andthenegativeerrorcodeincaseofa fatalerror.Otherwiseupdatesthebufferandtheeofstate.
if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
cmdstatp->flags &= 0xcf; /* No need for EOM in this case */
if (cmdstatp->flags != 0) { /* EOF, EOM, or ILI */ /* Compute the residual count */ if (cmdstatp->remainder_valid)
transfer = (int)cmdstatp->uremainder64; else
transfer = 0; if (cmdstatp->sense_hdr.sense_key == MEDIUM_ERROR) { if (STp->block_size == 0)
transfer = bytes; /* Some drives set ILI with MEDIUM ERROR */
cmdstatp->flags &= ~SENSE_ILI;
}
if (cmdstatp->flags & SENSE_ILI) { /* ILI */ if (STp->block_size == 0 &&
transfer < 0) {
st_printk(KERN_NOTICE, STp, "Failed to read %d " "byte block with %d " "byte transfer.\n",
bytes - transfer,
bytes); if (STps->drv_block >= 0)
STps->drv_block += 1;
STbp->buffer_bytes = 0; return (-ENOMEM);
} elseif (STp->block_size == 0) {
STbp->buffer_bytes = bytes - transfer;
} else {
st_release_request(SRpnt);
SRpnt = *aSRpnt = NULL; if (transfer == blks) { /* We did not get anything, error */
st_printk(KERN_NOTICE, STp, "Incorrect " "block size.\n"); 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(STp, "ILI but " "enough data " "received %ld " "%d.\n", count,
STbp->buffer_bytes); if (STps->drv_block >= 0)
STps->drv_block += 1; if (st_int_ioctl(STp, MTBSR, 1)) return (-EIO);
}
} elseif (cmdstatp->flags & SENSE_FMK) { /* 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(STp, "EOF detected (%d " "bytes read).\n",
STbp->buffer_bytes);
} elseif (cmdstatp->flags & SENSE_EOM) { 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(STp, "EOM detected (%d " "bytes read).\n",
STbp->buffer_bytes);
}
} /* end of EOF, EOM, ILI test */ else { /* nonzero sense key */
DEBC_printk(STp, "Tape error while reading.\n");
STps->drv_block = (-1); if (STps->eof == ST_FM &&
cmdstatp->sense_hdr.sense_key == BLANK_CHECK) {
DEBC_printk(STp, "Zero returned for " "first BLANK CHECK " "after EOF.\n");
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 (STp->sili) /* In fixed block mode residual is always zero here */
STbp->buffer_bytes -= STp->buffer->cmdstat.residual;
}
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;
}
if (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)
st_printk(ST_DEB_MSG, STp, "EOF up (%d). Left %d, needed %d.\n",
STps->eof, STbp->buffer_bytes,
(int)(count - total));
) /* end DEB */
transfer = STbp->buffer_bytes < count - total ?
STbp->buffer_bytes : count - total; if (!do_dio) {
i = from_buffer(STbp, buf, transfer); if (i) {
retval = i; goto out;
}
}
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++;
} elseif (STps->eof == ST_EOD_1) {
STps->eof = ST_EOD_2;
STps->drv_block = 0; if (STps->drv_file >= 0)
STps->drv_file++;
} elseif (STps->eof == ST_EOD_2)
STps->eof = ST_EOD;
} elseif (STps->eof == ST_FM)
STps->eof = ST_NOEOF;
retval = total;
/* Read a mode page into the tape buffer. The block descriptors are included ifincl_block_descsistrue.Thepagecontrolisoredtothepagenumber
parameter, if necessary. */ staticint read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
{ unsignedchar cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt;
/* 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. */ staticint write_mode_page(struct scsi_tape *STp, int page, int slow)
{ int pgo; unsignedchar cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt; int timeout;
/* Control the compression with mode page 15. Algorithm not changed if zero.
TheblockdescriptorsarereadandwrittenbecauseSonySDT-7000doesnot workwithoutthis(suggestionfromMichaelSchaefer<Michael.Schaefer@dlr.de>).
Including block descriptors should not cause any harm to other drives. */
staticint st_compression(struct scsi_tape * STp, int state)
{ int retval; int mpoffs; /* Offset to mode page start */ unsignedchar *b_data = (STp->buffer)->b_data;
if (STp->ready != ST_READY) return (-EIO);
/* Read the current page contents */
retval = read_mode_page(STp, COMPRESSION_PAGE, 0); if (retval) {
DEBC_printk(STp, "Compression mode page not supported.\n"); return (-EIO);
}
mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
DEBC_printk(STp, "Compression state is %d.\n",
(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(STp, "Compression not supported.\n"); 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, 0); if (retval) {
DEBC_printk(STp, "Compression change failed.\n"); return (-EIO);
}
DEBC_printk(STp, "Compression state changed to %d.\n", state);
STp->compression_changed = 1; return0;
}
/* Process the load and unload commands (does unload if the load code is zero) */ staticint do_load_unload(struct scsi_tape *STp, struct file *filp, int load_code)
{ int retval = (-EIO), timeout; unsignedchar cmd[MAX_COMMAND_SIZE]; struct st_partstat *STps; struct st_request *SRpnt;
if (STp->ready != ST_READY && !load_code) { if (STp->ready == ST_NO_TAPE) return (-ENOMEDIUM); else return (-EIO);
}
if (cmd_in == MTWEOF || cmd_in == MTWEOFI)
STps->rw = ST_IDLE; /* prevent automatic WEOF at close */
} else { /* SCSI command was not completely successful. Don't return
from this block without releasing the SCSI command block! */ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
/* Set the tape block and partition. Negative partition means that only the
block should be set in vendor specific way. */ staticint set_location(struct scsi_tape *STp, unsignedint block, int partition, int logical)
{ struct st_partstat *STps; int result, p; unsignedint blk; int timeout; unsignedchar scmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt;
/* Find the current partition number for the drive status. Called from open and
returns either partition number of negative error code. */ staticint find_partition(struct scsi_tape *STp)
{ int i, partition; unsignedint block;
if ((i = get_location(STp, &block, &partition, 1)) < 0) return i; if (partition >= ST_NBR_PARTITIONS) return (-EIO); return partition;
}
/* Change the partition if necessary */ staticint switch_partition(struct scsi_tape *STp)
{ struct st_partstat *STps;
if (STp->partition == STp->new_partition) return0;
STps = &(STp->ps[STp->new_partition]); if (!STps->last_block_valid)
STps->last_block_visited = 0; return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
}
/* Functions for reading and writing the medium partition mode page. */
/* Get the number of partitions on the tape. As a side effect reads the
mode page into the tape buffer. */ staticint nbr_partitions(struct scsi_tape *STp)
{ int result;
if (STp->ready != ST_READY) return (-EIO);
result = read_mode_page(STp, PART_PAGE, 1);
if (result) {
DEBC_printk(STp, "Can't read medium partition page.\n");
result = (-EIO);
} else {
result = (STp->buffer)->b_data[MODE_HEADER_LENGTH +
PP_OFF_NBR_ADD_PARTS] + 1;
DEBC_printk(STp, "Number of partitions %d.\n", result);
}
return result;
}
staticint format_medium(struct scsi_tape *STp, int format)
{ int result = 0; int timeout = STp->long_timeout; unsignedchar scmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt;
memset(scmd, 0, MAX_COMMAND_SIZE);
scmd[0] = FORMAT_UNIT;
scmd[2] = format; if (STp->immediate) {
scmd[1] |= 1; /* Don't wait for completion */
timeout = STp->device->request_queue->rq_timeout;
}
DEBC_printk(STp, "Sending FORMAT MEDIUM\n");
SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
timeout, MAX_RETRIES, 1); if (!SRpnt)
result = STp->buffer->syscall_result; return result;
}
/* Partition the tape into two partitions if size > 0 or one partition if size==0.
/* The mode page is in the buffer. Let's modify it and write it. */
bp = (STp->buffer)->b_data;
pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
DEBC_printk(STp, "Partition page length is %d bytes.\n",
bp[pgo + MP_OFF_PAGE_LENGTH] + 2);
if (scsi3) {
needs_format = (bp[pgo + PP_OFF_FLAGS] & PP_MSK_POFM) != 0; if (needs_format && size == 0) { /* No need to write the mode page when clearing *partitioning
*/
DEBC_printk(STp, "Formatting tape with one partition.\n");
result = format_medium(STp, 0); goto out;
} if (needs_format) /* Leave the old value for HP DATs claiming SCSI_3 */
psd_cnt = 2; if ((bp[pgo + PP_OFF_FLAGS] & PP_MSK_PSUM_UNITS) == PP_MSK_PSUM_UNITS) { /* Use units scaling for large partitions if the device *suggestsitandnoprecisionlost.RequiredforIBM *TS1140/50drivesthatdon'tsupportMBunits.
*/ if (size >= 1000 && (size % 1000) == 0) {
size /= 1000;
psum = PP_MSK_PSUM_UNITS;
units = 9; /* GB */
}
} /* Try it anyway if too large to specify in MB */ if (psum == PP_MSK_PSUM_MB && size >= 65534) {
size /= 1000;
psum = PP_MSK_PSUM_UNITS;
units = 9; /* GB */
}
}
if (size >= 65535 || /* Does not fit into two bytes */
(target_partition == 0 && psd_cnt < 2)) {
result = -EINVAL; goto out;
}
psdo = pgo + PART_PAGE_FIXED_LENGTH; /* The second condition is for HP DDS which use only one partition size *descriptor
*/ if (target_partition > 0 &&
(psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS] ||
bp[pgo + PP_OFF_MAX_ADD_PARTS] != 1)) {
bp[psdo] = bp[psdo + 1] = 0xff; /* Rest to partition 0 */
psdo += 2;
}
memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
switch (cmd_in) { case SG_IO: case SCSI_IOCTL_SEND_COMMAND: case CDROM_SEND_PACKET: if (!capable(CAP_SYS_RAWIO)) return -EPERM; break; default: break;
}
#ifdef CONFIG_COMPAT staticlong st_compat_ioctl(struct file *file, unsignedint cmd_in, unsignedlong arg)
{ /* argument conversion is handled using put_user_mtpos/put_user_mtget */ switch (cmd_in) { case MTIOCPOS32:
cmd_in = MTIOCPOS; break; case MTIOCGET32:
cmd_in = MTIOCGET; break;
}
return st_ioctl(file, cmd_in, arg);
} #endif
/* Try to allocate a new tape buffer. Calling function must not hold
dev_arr_lock. */ staticstruct st_buffer *new_tape_buffer(int max_sg)
{ struct st_buffer *tb;
/* Make sure that no data from previous user is in the internal buffer */ staticvoid clear_buffer(struct st_buffer * st_bp)
{ int i;
for (i=0; i < st_bp->frp_segs; i++)
memset(page_address(st_bp->reserved_pages[i]), 0,
PAGE_SIZE << st_bp->reserved_page_order);
st_bp->cleared = 1;
}
/* Release the extra buffer */ staticvoid normalize_buffer(struct st_buffer * STbuffer)
{ int i, order = STbuffer->reserved_page_order;
for (i = 0; i < STbuffer->frp_segs; i++) {
__free_pages(STbuffer->reserved_pages[i], order);
STbuffer->buffer_size -= (PAGE_SIZE << order);
}
STbuffer->frp_segs = 0;
STbuffer->sg_segs = 0;
STbuffer->reserved_page_order = 0;
STbuffer->map_data.offset = 0;
}
/* Move data from the user buffer to the tape buffer. Returns zero (success) or
negative error code. */ staticint append_to_buffer(constchar __user *ubp, struct st_buffer * st_bp, int do_count)
{ int i, cnt, res, offset; int length = PAGE_SIZE << st_bp->reserved_page_order;
for (i = 0, offset = st_bp->buffer_bytes;
i < st_bp->frp_segs && offset >= length; i++)
offset -= length; if (i == st_bp->frp_segs) { /* Should never happen */
printk(KERN_WARNING "st: append_to_buffer offset overflow.\n"); return (-EIO);
} for (; i < st_bp->frp_segs && do_count > 0; i++) { struct page *page = st_bp->reserved_pages[i];
cnt = length - offset < do_count ? length - offset : do_count;
res = copy_from_user(page_address(page) + offset, ubp, cnt); if (res) return (-EFAULT);
do_count -= cnt;
st_bp->buffer_bytes += cnt;
ubp += cnt;
offset = 0;
} if (do_count) /* Should never happen */ return (-EIO);
return0;
}
/* Move data from the tape buffer to the user buffer. Returns zero (success) or
negative error code. */ staticint from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count)
{ int i, cnt, res, offset; int length = PAGE_SIZE << st_bp->reserved_page_order;
for (i = 0, offset = st_bp->read_pointer;
i < st_bp->frp_segs && offset >= length; i++)
offset -= length; if (i == st_bp->frp_segs) { /* Should never happen */
printk(KERN_WARNING "st: from_buffer offset overflow.\n"); return (-EIO);
} for (; i < st_bp->frp_segs && do_count > 0; i++) { struct page *page = st_bp->reserved_pages[i];
cnt = length - offset < do_count ? length - offset : do_count;
res = copy_to_user(ubp, page_address(page) + offset, cnt); if (res) return (-EFAULT);
do_count -= cnt;
st_bp->buffer_bytes -= cnt;
st_bp->read_pointer += cnt;
ubp += cnt;
offset = 0;
} if (do_count) /* Should never happen */ return (-EIO);
return0;
}
/* Move data towards start of buffer */ staticvoid move_buffer_data(struct st_buffer * st_bp, int offset)
{ int src_seg, dst_seg, src_offset = 0, dst_offset; int count, total; int length = PAGE_SIZE << st_bp->reserved_page_order;
/* Validate the options from command line or module parameters */ staticvoid validate_options(void)
{ if (buffer_kbs > 0)
st_fixed_buffer_size = buffer_kbs * ST_KILOBYTE; if (max_sg_segs >= ST_FIRST_SG)
st_max_sg_segs = max_sg_segs;
}
#ifndef MODULE /* Set the boot options. Syntax is defined in Documenation/scsi/st.txt.
*/ staticint __init st_setup(char *str)
{ int i, len, ints[ARRAY_SIZE(parms) + 1]; char *stp;
stp = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) { for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++) if (parms[i].val)
*parms[i].val = ints[i + 1];
} else { while (stp != NULL) { for (i = 0; i < ARRAY_SIZE(parms); i++) {
len = strlen(parms[i].name); if (!strncmp(stp, parms[i].name, len) &&
(*(stp + len) == ':' || *(stp + len) == '=')) { if (parms[i].val)
*parms[i].val =
simple_strtoul(stp + len + 1, NULL, 0); else
printk(KERN_WARNING "st: Obsolete parameter %s\n",
parms[i].name); break;
}
} if (i >= ARRAY_SIZE(parms))
printk(KERN_WARNING "st: invalid parameter in '%s'\n",
stp);
stp = strchr(stp, ','); if (stp)
stp++;
}
}
if (SDp->type != TYPE_TAPE) return -ENODEV; if (st_incompatible(SDp)) {
sdev_printk(KERN_INFO, SDp, "OnStream tapes are no longer supported;\n");
sdev_printk(KERN_INFO, SDp, "please mail to linux-scsi@vger.kernel.org.\n"); return -ENODEV;
}
scsi_autopm_get_device(SDp);
i = queue_max_segments(SDp->request_queue); if (st_max_sg_segs < i)
i = st_max_sg_segs;
buffer = new_tape_buffer(i); if (buffer == NULL) {
sdev_printk(KERN_ERR, SDp, "st: Can't allocate new tape buffer. " "Device not attached.\n"); goto out;
}
err = class_register(&st_sysfs_class); if (err) {
pr_err("Unable register sysfs class for SCSI tapes\n"); return err;
}
err = register_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
ST_MAX_TAPE_ENTRIES, "st"); if (err) {
printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
SCSI_TAPE_MAJOR); goto err_class;
}
err = scsi_register_driver(&st_template.gendrv); if (err) goto err_chrdev;
#if DEBUG static ssize_t debug_flag_store(struct device_driver *ddp, constchar *buf, size_t count)
{ /* We only care what the first byte of the data is the rest is unused. *ifit'sa'1'weturnondebugandifit'sa'0'wedisableit.All *othervalueshave-EINVALreturnediftheyarepassedin.
*/ if (count > 0) { if (buf[0] == '0') {
debugging = NO_DEBUG; return count;
} elseif (buf[0] == '1') {
debugging = 1; return count;
}
} return -EINVAL;
}
/* Try to fault in all of the necessary pages */ /* rw==READ means read from drive, write into memory area */
res = pin_user_pages_fast(uaddr, nr_pages, rw == READ ? FOLL_WRITE : 0,
pages);
/* Errors and no page mapped should return here */ if (res < nr_pages) goto out_unmap;
for (i=0; i < nr_pages; i++) { /* FIXME: flush superflous for rw==READ, *probablywrongfunctionforrw==WRITE
*/
flush_dcache_page(pages[i]);
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.128Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.