diff options
Diffstat (limited to 'drivers/scsi/osst.c')
-rw-r--r-- | drivers/scsi/osst.c | 6108 |
1 files changed, 0 insertions, 6108 deletions
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c deleted file mode 100644 index 815bb4097c1b..000000000000 --- a/drivers/scsi/osst.c +++ /dev/null @@ -1,6108 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying - file Documentation/scsi/st.txt for more information. - - History: - - OnStream SCSI Tape support (osst) cloned from st.c by - Willem Riede (osst@riede.org) Feb 2000 - Fixes ... Kurt Garloff <garloff@suse.de> Mar 2000 - - Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara. - Contribution and ideas from several people including (in alphabetical - order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer, - Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale. - - Copyright 1992 - 2002 Kai Makisara / 2000 - 2006 Willem Riede - email osst@riede.org - - $Header: /cvsroot/osst/Driver/osst.c,v 1.73 2005/01/01 21:13:34 wriede Exp $ - - Microscopic alterations - Rik Ling, 2000/12/21 - Last st.c sync: Tue Oct 15 22:01:04 2002 by makisara - Some small formal changes - aeb, 950809 -*/ - -static const char * cvsid = "$Id: osst.c,v 1.73 2005/01/01 21:13:34 wriede Exp $"; -static const char * osst_version = "0.99.4"; - -/* The "failure to reconnect" firmware bug */ -#define OSST_FW_NEED_POLL_MIN 10601 /*(107A)*/ -#define OSST_FW_NEED_POLL_MAX 10704 /*(108D)*/ -#define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7) - -#include <linux/module.h> - -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/sched/signal.h> -#include <linux/proc_fs.h> -#include <linux/mm.h> -#include <linux/slab.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/vmalloc.h> -#include <linux/blkdev.h> -#include <linux/moduleparam.h> -#include <linux/delay.h> -#include <linux/jiffies.h> -#include <linux/mutex.h> -#include <linux/uaccess.h> -#include <asm/dma.h> - -/* The driver prints some debugging information on the console if DEBUG - is defined and non-zero. */ -#define DEBUG 0 - -/* 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 OSST_DEB_MSG KERN_NOTICE - -#include <scsi/scsi.h> -#include <scsi/scsi_dbg.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_driver.h> -#include <scsi/scsi_eh.h> -#include <scsi/scsi_host.h> -#include <scsi/scsi_ioctl.h> - -#define ST_KILOBYTE 1024 - -#include "st.h" -#include "osst.h" -#include "osst_options.h" -#include "osst_detect.h" - -static DEFINE_MUTEX(osst_int_mutex); -static int max_dev = 0; -static int write_threshold_kbs = 0; -static int max_sg_segs = 0; - -#ifdef MODULE -MODULE_AUTHOR("Willem Riede"); -MODULE_DESCRIPTION("OnStream {DI-|FW-|SC-|USB}{30|50} Tape Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(OSST_MAJOR); -MODULE_ALIAS_SCSI_DEVICE(TYPE_TAPE); - -module_param(max_dev, int, 0444); -MODULE_PARM_DESC(max_dev, "Maximum number of OnStream Tape Drives to attach (4)"); - -module_param(write_threshold_kbs, int, 0644); -MODULE_PARM_DESC(write_threshold_kbs, "Asynchronous write threshold (KB; 32)"); - -module_param(max_sg_segs, int, 0644); -MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (9)"); -#else -static struct osst_dev_parm { - char *name; - int *val; -} parms[] __initdata = { - { "max_dev", &max_dev }, - { "write_threshold_kbs", &write_threshold_kbs }, - { "max_sg_segs", &max_sg_segs } -}; -#endif - -/* Some default definitions have been moved to osst_options.h */ -#define OSST_BUFFER_SIZE (OSST_BUFFER_BLOCKS * ST_KILOBYTE) -#define OSST_WRITE_THRESHOLD (OSST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE) - -/* The buffer size should fit into the 24 bits for length in the - 6-byte SCSI read and write commands. */ -#if OSST_BUFFER_SIZE >= (2 << 24 - 1) -#error "Buffer size should not exceed (2 << 24 - 1) bytes!" -#endif - -#if DEBUG -static int debugging = 1; -/* uncomment define below to test error recovery */ -// #define OSST_INJECT_ERRORS 1 -#endif - -/* Do not retry! The drive firmware already retries when appropriate, - and when it tries to tell us something, we had better listen... */ -#define MAX_RETRIES 0 - -#define NO_TAPE NOT_READY - -#define OSST_WAIT_POSITION_COMPLETE (HZ > 200 ? HZ / 200 : 1) -#define OSST_WAIT_WRITE_COMPLETE (HZ / 12) -#define OSST_WAIT_LONG_WRITE_COMPLETE (HZ / 2) - -#define OSST_TIMEOUT (200 * HZ) -#define OSST_LONG_TIMEOUT (1800 * HZ) - -#define TAPE_NR(x) (iminor(x) & ((1 << ST_MODE_SHIFT)-1)) -#define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT) -#define TAPE_REWIND(x) ((iminor(x) & 0x80) == 0) -#define TAPE_IS_RAW(x) (TAPE_MODE(x) & (ST_NBR_MODES >> 1)) - -/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower - 24 bits) */ -#define SET_DENS_AND_BLK 0x10001 - -static int osst_buffer_size = OSST_BUFFER_SIZE; -static int osst_write_threshold = OSST_WRITE_THRESHOLD; -static int osst_max_sg_segs = OSST_MAX_SG; -static int osst_max_dev = OSST_MAX_TAPES; -static int osst_nr_dev; - -static struct osst_tape **os_scsi_tapes = NULL; -static DEFINE_RWLOCK(os_scsi_tapes_lock); - -static int modes_defined = 0; - -static struct osst_buffer *new_tape_buffer(int, int, int); -static int enlarge_buffer(struct osst_buffer *, int); -static void normalize_buffer(struct osst_buffer *); -static int append_to_buffer(const char __user *, struct osst_buffer *, int); -static int from_buffer(struct osst_buffer *, char __user *, int); -static int osst_zero_buffer_tail(struct osst_buffer *); -static int osst_copy_to_buffer(struct osst_buffer *, unsigned char *); -static int osst_copy_from_buffer(struct osst_buffer *, unsigned char *); - -static int osst_probe(struct device *); -static int osst_remove(struct device *); - -static struct scsi_driver osst_template = { - .gendrv = { - .name = "osst", - .owner = THIS_MODULE, - .probe = osst_probe, - .remove = osst_remove, - } -}; - -static int osst_int_ioctl(struct osst_tape *STp, struct osst_request ** aSRpnt, - unsigned int cmd_in, unsigned long arg); - -static int osst_set_frame_position(struct osst_tape *STp, struct osst_request ** aSRpnt, int frame, int skip); - -static int osst_get_frame_position(struct osst_tape *STp, struct osst_request ** aSRpnt); - -static int osst_flush_write_buffer(struct osst_tape *STp, struct osst_request ** aSRpnt); - -static int osst_write_error_recovery(struct osst_tape * STp, struct osst_request ** aSRpnt, int pending); - -static inline char *tape_name(struct osst_tape *tape) -{ - return tape->drive->disk_name; -} - -/* Routines that handle the interaction with mid-layer SCSI routines */ - - -/* Normalize Sense */ -static void osst_analyze_sense(struct osst_request *SRpnt, struct st_cmdstatus *s) -{ - const u8 *ucp; - const u8 *sense = SRpnt->sense; - - s->have_sense = scsi_normalize_sense(SRpnt->sense, - SCSI_SENSE_BUFFERSIZE, &s->sense_hdr); - s->flags = 0; - - if (s->have_sense) { - s->deferred = 0; - s->remainder_valid = - scsi_get_sense_info_fld(sense, SCSI_SENSE_BUFFERSIZE, &s->uremainder64); - switch (sense[0] & 0x7f) { - case 0x71: - s->deferred = 1; - /* fall through */ - case 0x70: - s->fixed_format = 1; - s->flags = sense[2] & 0xe0; - break; - case 0x73: - s->deferred = 1; - /* fall through */ - case 0x72: - s->fixed_format = 0; - ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4); - s->flags = ucp ? (ucp[3] & 0xe0) : 0; - break; - } - } -} - -/* Convert the result to success code */ -static int osst_chk_result(struct osst_tape * STp, struct osst_request * SRpnt) -{ - char *name = tape_name(STp); - int result = SRpnt->result; - u8 * sense = SRpnt->sense, scode; -#if DEBUG - const char *stp; -#endif - struct st_cmdstatus *cmdstatp; - - if (!result) - return 0; - - cmdstatp = &STp->buffer->cmdstat; - osst_analyze_sense(SRpnt, cmdstatp); - - if (cmdstatp->have_sense) - scode = STp->buffer->cmdstat.sense_hdr.sense_key; - else - scode = 0; -#if DEBUG - if (debugging) { - printk(OSST_DEB_MSG "%s:D: Error: %x, cmd: %x %x %x %x %x %x\n", - name, result, - SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2], - SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]); - if (scode) printk(OSST_DEB_MSG "%s:D: Sense: %02x, ASC: %02x, ASCQ: %02x\n", - name, scode, sense[12], sense[13]); - if (cmdstatp->have_sense) - __scsi_print_sense(STp->device, name, - SRpnt->sense, SCSI_SENSE_BUFFERSIZE); - } - else -#endif - if (cmdstatp->have_sense && ( - scode != NO_SENSE && - scode != RECOVERED_ERROR && -/* scode != UNIT_ATTENTION && */ - scode != BLANK_CHECK && - scode != VOLUME_OVERFLOW && - SRpnt->cmd[0] != MODE_SENSE && - SRpnt->cmd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */ - if (cmdstatp->have_sense) { - printk(KERN_WARNING "%s:W: Command with sense data:\n", name); - __scsi_print_sense(STp->device, name, - SRpnt->sense, SCSI_SENSE_BUFFERSIZE); - } - else { - static int notyetprinted = 1; - - printk(KERN_WARNING - "%s:W: Warning %x (driver bt 0x%x, host bt 0x%x).\n", - name, result, driver_byte(result), - host_byte(result)); - if (notyetprinted) { - notyetprinted = 0; - printk(KERN_INFO - "%s:I: This warning may be caused by your scsi controller,\n", name); - printk(KERN_INFO - "%s:I: it has been reported with some Buslogic cards.\n", name); - } - } - } - STp->pos_unknown |= STp->device->was_reset; - - if (cmdstatp->have_sense && scode == RECOVERED_ERROR) { - STp->recover_count++; - STp->recover_erreg++; -#if DEBUG - if (debugging) { - if (SRpnt->cmd[0] == READ_6) - stp = "read"; - else if (SRpnt->cmd[0] == WRITE_6) - stp = "write"; - else - stp = "ioctl"; - printk(OSST_DEB_MSG "%s:D: Recovered %s error (%d).\n", name, stp, - STp->recover_count); - } -#endif - if ((sense[2] & 0xe0) == 0) - return 0; - } - return (-EIO); -} - - -/* Wakeup from interrupt */ -static void osst_end_async(struct request *req, blk_status_t status) -{ - struct scsi_request *rq = scsi_req(req); - struct osst_request *SRpnt = req->end_io_data; - struct osst_tape *STp = SRpnt->stp; - struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data; - - STp->buffer->cmdstat.midlevel_result = SRpnt->result = rq->result; -#if DEBUG - STp->write_pending = 0; -#endif - if (rq->sense_len) - memcpy(SRpnt->sense, rq->sense, SCSI_SENSE_BUFFERSIZE); - if (SRpnt->waiting) - complete(SRpnt->waiting); - - if (SRpnt->bio) { - kfree(mdata->pages); - blk_rq_unmap_user(SRpnt->bio); - } - - blk_put_request(req); -} - -/* osst_request memory management */ -static struct osst_request *osst_allocate_request(void) -{ - return kzalloc(sizeof(struct osst_request), GFP_KERNEL); -} - -static void osst_release_request(struct osst_request *streq) -{ - kfree(streq); -} - -static int osst_execute(struct osst_request *SRpnt, const unsigned char *cmd, - int cmd_len, int data_direction, void *buffer, unsigned bufflen, - int use_sg, int timeout, int retries) -{ - struct request *req; - struct scsi_request *rq; - struct page **pages = NULL; - struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data; - - int err = 0; - int write = (data_direction == DMA_TO_DEVICE); - - req = blk_get_request(SRpnt->stp->device->request_queue, - write ? REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0); - if (IS_ERR(req)) - return DRIVER_ERROR << 24; - - rq = scsi_req(req); - req->rq_flags |= RQF_QUIET; - - SRpnt->bio = NULL; - - if (use_sg) { - struct scatterlist *sg, *sgl = (struct scatterlist *)buffer; - int i; - - pages = kcalloc(use_sg, sizeof(struct page *), GFP_KERNEL); - if (!pages) - goto free_req; - - for_each_sg(sgl, sg, use_sg, i) - pages[i] = sg_page(sg); - - mdata->null_mapped = 1; - - mdata->page_order = get_order(sgl[0].length); - mdata->nr_entries = - DIV_ROUND_UP(bufflen, PAGE_SIZE << mdata->page_order); - mdata->offset = 0; - - err = blk_rq_map_user(req->q, req, mdata, NULL, bufflen, GFP_KERNEL); - if (err) { - kfree(pages); - goto free_req; - } - SRpnt->bio = req->bio; - mdata->pages = pages; - - } else if (bufflen) { - err = blk_rq_map_kern(req->q, req, buffer, bufflen, GFP_KERNEL); - if (err) - goto free_req; - } - - rq->cmd_len = cmd_len; - memset(rq->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */ - memcpy(rq->cmd, cmd, rq->cmd_len); - req->timeout = timeout; - rq->retries = retries; - req->end_io_data = SRpnt; - - blk_execute_rq_nowait(req->q, NULL, req, 1, osst_end_async); - return 0; -free_req: - blk_put_request(req); - return DRIVER_ERROR << 24; -} - -/* Do the scsi command. Waits until command performed if do_wait is true. - Otherwise osst_write_behind_check() is used to check that the command - has finished. */ -static struct osst_request * osst_do_scsi(struct osst_request *SRpnt, struct osst_tape *STp, - unsigned char *cmd, int bytes, int direction, int timeout, int retries, int do_wait) -{ - unsigned char *bp; - unsigned short use_sg; -#ifdef OSST_INJECT_ERRORS - static int inject = 0; - static int repeat = 0; -#endif - struct completion *waiting; - - /* if async, make sure there's no command outstanding */ - if (!do_wait && ((STp->buffer)->last_SRpnt)) { - printk(KERN_ERR "%s: Async command already active.\n", - tape_name(STp)); - if (signal_pending(current)) - (STp->buffer)->syscall_result = (-EINTR); - else - (STp->buffer)->syscall_result = (-EBUSY); - return NULL; - } - - if (SRpnt == NULL) { - SRpnt = osst_allocate_request(); - if (SRpnt == NULL) { - printk(KERN_ERR "%s: Can't allocate SCSI request.\n", - tape_name(STp)); - if (signal_pending(current)) - (STp->buffer)->syscall_result = (-EINTR); - else - (STp->buffer)->syscall_result = (-EBUSY); - return NULL; - } - SRpnt->stp = STp; - } - - /* 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; - - waiting = &STp->wait; - init_completion(waiting); - SRpnt->waiting = waiting; - - use_sg = (bytes > STp->buffer->sg[0].length) ? STp->buffer->use_sg : 0; - if (use_sg) { - bp = (char *)&(STp->buffer->sg[0]); - if (STp->buffer->sg_segs < use_sg) - use_sg = STp->buffer->sg_segs; - } - else - bp = (STp->buffer)->b_data; - - memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd)); - STp->buffer->cmdstat.have_sense = 0; - STp->buffer->syscall_result = 0; - - if (osst_execute(SRpnt, cmd, COMMAND_SIZE(cmd[0]), direction, bp, bytes, - use_sg, timeout, retries)) - /* could not allocate the buffer or request was too large */ - (STp->buffer)->syscall_result = (-EBUSY); - else if (do_wait) { - wait_for_completion(waiting); - SRpnt->waiting = NULL; - STp->buffer->syscall_result = osst_chk_result(STp, SRpnt); -#ifdef OSST_INJECT_ERRORS - if (STp->buffer->syscall_result == 0 && - cmd[0] == READ_6 && - cmd[4] && - ( (++ inject % 83) == 29 || - (STp->first_frame_position == 240 - /* or STp->read_error_frame to fail again on the block calculated above */ && - ++repeat < 3))) { - printk(OSST_DEB_MSG "%s:D: Injecting read error\n", tape_name(STp)); - STp->buffer->last_result_fatal = 1; - } -#endif - } - return SRpnt; -} - - -/* Handle the write-behind checking (downs the semaphore) */ -static void osst_write_behind_check(struct osst_tape *STp) -{ - struct osst_buffer * STbuffer; - - STbuffer = STp->buffer; - -#if DEBUG - if (STp->write_pending) - STp->nbr_waits++; - else - STp->nbr_finished++; -#endif - wait_for_completion(&(STp->wait)); - STp->buffer->last_SRpnt->waiting = NULL; - - STp->buffer->syscall_result = osst_chk_result(STp, STp->buffer->last_SRpnt); - - if (STp->buffer->syscall_result) - STp->buffer->syscall_result = - osst_write_error_recovery(STp, &(STp->buffer->last_SRpnt), 1); - else - STp->first_frame_position++; - - osst_release_request(STp->buffer->last_SRpnt); - - if (STbuffer->writing < STbuffer->buffer_bytes) - printk(KERN_WARNING "osst :A: write_behind_check: something left in buffer!\n"); - - STbuffer->last_SRpnt = NULL; - STbuffer->buffer_bytes -= STbuffer->writing; - STbuffer->writing = 0; - - return; -} - - - -/* Onstream specific Routines */ -/* - * Initialize the OnStream AUX - */ -static void osst_init_aux(struct osst_tape * STp, int frame_type, int frame_seq_number, - int logical_blk_num, int blk_sz, int blk_cnt) -{ - os_aux_t *aux = STp->buffer->aux; - os_partition_t *par = &aux->partition; - os_dat_t *dat = &aux->dat; - - if (STp->raw) return; - - memset(aux, 0, sizeof(*aux)); - aux->format_id = htonl(0); - memcpy(aux->application_sig, "LIN4", 4); - aux->hdwr = htonl(0); - aux->frame_type = frame_type; - - switch (frame_type) { - case OS_FRAME_TYPE_HEADER: - aux->update_frame_cntr = htonl(STp->update_frame_cntr); - par->partition_num = OS_CONFIG_PARTITION; - par->par_desc_ver = OS_PARTITION_VERSION; - par->wrt_pass_cntr = htons(0xffff); - /* 0-4 = reserved, 5-9 = header, 2990-2994 = header, 2995-2999 = reserved */ - par->first_frame_ppos = htonl(0); - par->last_frame_ppos = htonl(0xbb7); - aux->frame_seq_num = htonl(0); - aux->logical_blk_num_high = htonl(0); - aux->logical_blk_num = htonl(0); - aux->next_mark_ppos = htonl(STp->first_mark_ppos); - break; - case OS_FRAME_TYPE_DATA: - case OS_FRAME_TYPE_MARKER: - dat->dat_sz = 8; - dat->reserved1 = 0; - dat->entry_cnt = 1; - dat->reserved3 = 0; - dat->dat_list[0].blk_sz = htonl(blk_sz); - dat->dat_list[0].blk_cnt = htons(blk_cnt); - dat->dat_list[0].flags = frame_type==OS_FRAME_TYPE_MARKER? - OS_DAT_FLAGS_MARK:OS_DAT_FLAGS_DATA; - dat->dat_list[0].reserved = 0; - /* fall through */ - case OS_FRAME_TYPE_EOD: - aux->update_frame_cntr = htonl(0); - par->partition_num = OS_DATA_PARTITION; - par->par_desc_ver = OS_PARTITION_VERSION; - par->wrt_pass_cntr = htons(STp->wrt_pass_cntr); - par->first_frame_ppos = htonl(STp->first_data_ppos); - par->last_frame_ppos = htonl(STp->capacity); - aux->frame_seq_num = htonl(frame_seq_number); - aux->logical_blk_num_high = htonl(0); - aux->logical_blk_num = htonl(logical_blk_num); - break; - default: ; /* probably FILL */ - } - aux->filemark_cnt = htonl(STp->filemark_cnt); - aux->phys_fm = htonl(0xffffffff); - aux->last_mark_ppos = htonl(STp->last_mark_ppos); - aux->last_mark_lbn = htonl(STp->last_mark_lbn); -} - -/* - * Verify that we have the correct tape frame - */ -static int osst_verify_frame(struct osst_tape * STp, int frame_seq_number, int quiet) -{ - char * name = tape_name(STp); - os_aux_t * aux = STp->buffer->aux; - os_partition_t * par = &(aux->partition); - struct st_partstat * STps = &(STp->ps[STp->partition]); - unsigned int blk_cnt, blk_sz, i; - - if (STp->raw) { - if (STp->buffer->syscall_result) { - for (i=0; i < STp->buffer->sg_segs; i++) - memset(page_address(sg_page(&STp->buffer->sg[i])), - 0, STp->buffer->sg[i].length); - strcpy(STp->buffer->b_data, "READ ERROR ON FRAME"); - } else - STp->buffer->buffer_bytes = OS_FRAME_SIZE; - return 1; - } - if (STp->buffer->syscall_result) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, read error\n", name); -#endif - return 0; - } - if (ntohl(aux->format_id) != 0) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, format_id %u\n", name, ntohl(aux->format_id)); -#endif - goto err_out; - } - if (memcmp(aux->application_sig, STp->application_sig, 4) != 0 && - (memcmp(aux->application_sig, "LIN3", 4) != 0 || STp->linux_media_version != 4)) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, incorrect application signature\n", name); -#endif - goto err_out; - } - if (par->partition_num != OS_DATA_PARTITION) { - if (!STp->linux_media || STp->linux_media_version != 2) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, partition num %d\n", - name, par->partition_num); -#endif - goto err_out; - } - } - if (par->par_desc_ver != OS_PARTITION_VERSION) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, partition version %d\n", name, par->par_desc_ver); -#endif - goto err_out; - } - if (ntohs(par->wrt_pass_cntr) != STp->wrt_pass_cntr) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, wrt_pass_cntr %d (expected %d)\n", - name, ntohs(par->wrt_pass_cntr), STp->wrt_pass_cntr); -#endif - goto err_out; - } - if (aux->frame_type != OS_FRAME_TYPE_DATA && - aux->frame_type != OS_FRAME_TYPE_EOD && - aux->frame_type != OS_FRAME_TYPE_MARKER) { - if (!quiet) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, frame type %x\n", name, aux->frame_type); -#endif - } - goto err_out; - } - if (aux->frame_type == OS_FRAME_TYPE_EOD && - STp->first_frame_position < STp->eod_frame_ppos) { - printk(KERN_INFO "%s:I: Skipping premature EOD frame %d\n", name, - STp->first_frame_position); - goto err_out; - } - if (frame_seq_number != -1 && ntohl(aux->frame_seq_num) != frame_seq_number) { - if (!quiet) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Skipping frame, sequence number %u (expected %d)\n", - name, ntohl(aux->frame_seq_num), frame_seq_number); -#endif - } - goto err_out; - } - if (aux->frame_type == OS_FRAME_TYPE_MARKER) { - STps->eof = ST_FM_HIT; - - i = ntohl(aux->filemark_cnt); - if (STp->header_cache != NULL && i < OS_FM_TAB_MAX && (i > STp->filemark_cnt || - STp->first_frame_position - 1 != ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i]))) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: %s filemark %d at frame pos %d\n", name, - STp->header_cache->dat_fm_tab.fm_tab_ent[i] == 0?"Learned":"Corrected", - i, STp->first_frame_position - 1); -#endif - STp->header_cache->dat_fm_tab.fm_tab_ent[i] = htonl(STp->first_frame_position - 1); - if (i >= STp->filemark_cnt) - STp->filemark_cnt = i+1; - } - } - if (aux->frame_type == OS_FRAME_TYPE_EOD) { - STps->eof = ST_EOD_1; - STp->frame_in_buffer = 1; - } - if (aux->frame_type == OS_FRAME_TYPE_DATA) { - blk_cnt = ntohs(aux->dat.dat_list[0].blk_cnt); - blk_sz = ntohl(aux->dat.dat_list[0].blk_sz); - STp->buffer->buffer_bytes = blk_cnt * blk_sz; - STp->buffer->read_pointer = 0; - STp->frame_in_buffer = 1; - - /* See what block size was used to write file */ - if (STp->block_size != blk_sz && blk_sz > 0) { - printk(KERN_INFO - "%s:I: File was written with block size %d%c, currently %d%c, adjusted to match.\n", - name, blk_sz<1024?blk_sz:blk_sz/1024,blk_sz<1024?'b':'k', - STp->block_size<1024?STp->block_size:STp->block_size/1024, - STp->block_size<1024?'b':'k'); - STp->block_size = blk_sz; - STp->buffer->buffer_blocks = OS_DATA_SIZE / blk_sz; - } - STps->eof = ST_NOEOF; - } - STp->frame_seq_number = ntohl(aux->frame_seq_num); - STp->logical_blk_num = ntohl(aux->logical_blk_num); - return 1; - -err_out: - if (STp->read_error_frame == 0) - STp->read_error_frame = STp->first_frame_position - 1; - return 0; -} - -/* - * Wait for the unit to become Ready - */ -static int osst_wait_ready(struct osst_tape * STp, struct osst_request ** aSRpnt, - unsigned timeout, int initial_delay) -{ - unsigned char cmd[MAX_COMMAND_SIZE]; - struct osst_request * SRpnt; - unsigned long startwait = jiffies; -#if DEBUG - int dbg = debugging; - char * name = tape_name(STp); - - printk(OSST_DEB_MSG "%s:D: Reached onstream wait ready\n", name); -#endif - - if (initial_delay > 0) - msleep(jiffies_to_msecs(initial_delay)); - - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = TEST_UNIT_READY; - - SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1); - *aSRpnt = SRpnt; - if (!SRpnt) return (-EBUSY); - - while ( STp->buffer->syscall_result && time_before(jiffies, startwait + timeout*HZ) && - (( SRpnt->sense[2] == 2 && SRpnt->sense[12] == 4 && - (SRpnt->sense[13] == 1 || SRpnt->sense[13] == 8) ) || - ( SRpnt->sense[2] == 6 && SRpnt->sense[12] == 0x28 && - SRpnt->sense[13] == 0 ) )) { -#if DEBUG - if (debugging) { - printk(OSST_DEB_MSG "%s:D: Sleeping in onstream wait ready\n", name); - printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name); - debugging = 0; - } -#endif - msleep(100); - - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = TEST_UNIT_READY; - - SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1); - } - *aSRpnt = SRpnt; -#if DEBUG - debugging = dbg; -#endif - if ( STp->buffer->syscall_result && - osst_write_error_recovery(STp, aSRpnt, 0) ) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Abnormal exit from onstream wait ready\n", name); - printk(OSST_DEB_MSG "%s:D: Result = %d, Sense: 0=%02x, 2=%02x, 12=%02x, 13=%02x\n", name, - STp->buffer->syscall_result, SRpnt->sense[0], SRpnt->sense[2], - SRpnt->sense[12], SRpnt->sense[13]); -#endif - return (-EIO); - } -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Normal exit from onstream wait ready\n", name); -#endif - return 0; -} - -/* - * Wait for a tape to be inserted in the unit - */ -static int osst_wait_for_medium(struct osst_tape * STp, struct osst_request ** aSRpnt, unsigned timeout) -{ - unsigned char cmd[MAX_COMMAND_SIZE]; - struct osst_request * SRpnt; - unsigned long startwait = jiffies; -#if DEBUG - int dbg = debugging; - char * name = tape_name(STp); - - printk(OSST_DEB_MSG "%s:D: Reached onstream wait for medium\n", name); -#endif - - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = TEST_UNIT_READY; - - SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1); - *aSRpnt = SRpnt; - if (!SRpnt) return (-EBUSY); - - while ( STp->buffer->syscall_result && time_before(jiffies, startwait + timeout*HZ) && - SRpnt->sense[2] == 2 && SRpnt->sense[12] == 0x3a && SRpnt->sense[13] == 0 ) { -#if DEBUG - if (debugging) { - printk(OSST_DEB_MSG "%s:D: Sleeping in onstream wait medium\n", name); - printk(OSST_DEB_MSG "%s:D: Turning off debugging for a while\n", name); - debugging = 0; - } -#endif - msleep(100); - - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = TEST_UNIT_READY; - - SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1); - } - *aSRpnt = SRpnt; -#if DEBUG - debugging = dbg; -#endif - if ( STp->buffer->syscall_result && SRpnt->sense[2] != 2 && - SRpnt->sense[12] != 4 && SRpnt->sense[13] == 1) { -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Abnormal exit from onstream wait medium\n", name); - printk(OSST_DEB_MSG "%s:D: Result = %d, Sense: 0=%02x, 2=%02x, 12=%02x, 13=%02x\n", name, - STp->buffer->syscall_result, SRpnt->sense[0], SRpnt->sense[2], - SRpnt->sense[12], SRpnt->sense[13]); -#endif - return 0; - } -#if DEBUG - printk(OSST_DEB_MSG "%s:D: Normal exit from onstream wait medium\n", name); -#endif - return 1; -} - -static int osst_position_tape_and_confirm(struct osst_tape * STp, struct osst_request ** aSRpnt, int frame) -{ - int retval; - - osst_wait_ready(STp, aSRpnt, 15 * 60, 0); /* TODO - can this catch a write error? */ - retval = osst_set_frame_position(STp, aSRpnt, frame, 0); - if (retval) return (retval); - osst_wait_ready(STp, aSRpnt, 15 * 60, OSST_WAIT_POSITION_COMPLETE); - return (osst_get_frame_position(STp, aSRpnt)); -} - -/* - * Wait for write(s) to complete - */ -static int osst_flush_drive_buffer(struct osst_tape * STp, struct osst_request ** aSRpnt) -{ - unsigned char cmd[MAX_COMMAND_SIZE]; - struct osst_request * SRpnt; - int result = 0; - int delay = OSST_WAIT_WRITE_COMPLETE; -#if DEBUG - char * name = tape_name(STp); - - printk(OSST_DEB_MSG "%s:D: Reached onstream flush drive buffer (write filemark)\n", name); -#endif - - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = WRITE_FILEMARKS; - cmd[1] = 1; - - SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, MAX_RETRIES, 1); - *aSRpnt = SRpnt; - if (!SRpnt) return (-EBUSY); - if (STp->buffer->syscall_result) { - if ((SRpnt->sense[2] & 0x0f) == 2 && SRpnt->sense[12] == 4) { - if (SRpnt->sense[13] == 8) { - delay = OSST_WAIT_LONG_WRITE_COMPLETE; - } - } else - result = osst_write_error_recovery(STp, aSRpnt, 0); - } - result |= osst_wait_ready(STp, aSRpnt, 5 * 60, delay); - STp->ps[STp->partition].rw = OS_WRITING_COMPLETE; - - return (result); -} - -#define OSST_POLL_PER_SEC 10 -static int osst_wait_frame(struct osst_tape * STp, struct osst_request ** aSRpnt, int curr, int minlast, int to) -{ - unsigned long startwait = jiffies; - char * name = tape_name(STp); -#if DEBUG - char notyetprinted = 1; -#endif - if (minlast >= 0 && STp->ps[STp->partition].rw != ST_READING) - printk(KERN_ERR "%s:A: Waiting for frame without having initialized read!\n", name); - - while (time_before (jiffies, startwait + to*HZ)) - { - int result; - result = osst_get_frame_position(STp, aSRpnt); - if (result == -EIO) - if ((result = osst_write_error_recovery(STp, aSRpnt, 0)) == 0) - return 0; /* successful recovery leaves drive ready for frame */ - if (result < 0) break; - if (STp->first_frame_position == curr && - ((minlast < 0 && - (signed)STp->last_frame_position > (signed)curr + minlast) || - (minlast >= 0 && STp->cur_frames > minlast) - ) && result >= 0) - { -#if DEBUG - if (debugging || time_after_eq(jiffies, startwait + 2*HZ/OSST_POLL_PER_SEC)) - printk (OSST_DEB_MSG - "%s:D: Succ wait f fr %i (>%i): %i-%i %i (%i): %3li.%li s\n", - name, curr, curr+minlast, STp->first_frame_position, - STp->last_frame_position, STp->cur_frames, - result, (jiffies-startwait)/HZ, - (((jiffies-startwait)%HZ)*10)/HZ); -#endif - return 0; - } -#if DEBUG - if (time_after_eq(jiffies, startwait + 2*HZ/OSST_POLL_PER_SEC) && notyetprinted) - { - printk (OSST_DEB_MSG "%s:D: Wait for frame %i (>%i): %i-%i %i (%i)\n", - name, curr, curr+minlast, STp->first_frame_position, - STp->last_frame_position, STp->cur_frames, result); - notyetprinted--; - } -#endif - msleep(1000 / OSST_POLL_PER_SEC); - } -#if DEBUG - printk (OSST_DEB_MSG "%s:D: Fail wait f fr %i (>%i): %i-%i %i: %3li.%li s\n", - name, curr, curr+minlast, STp->first_frame_position, - STp->last_frame_position, STp->cur_frames, - (jiffies-startwait)/HZ, (((jiffies-startwait)%HZ)*10)/HZ); -#endif - return -EBUSY; -} - -static int osst_recover_wait_frame(struct osst_tape * STp, struct osst_request ** aSRpnt, int writing) -{ - struct osst_request * SRpnt; - unsigned char cmd[MAX_COMMAND_SIZE]; - unsigned long startwait = jiffies; - int retval = 1; - char * name = tape_name(STp); - - if (writing) { - char mybuf[24]; - char * olddata = STp->buffer->b_data; - int oldsize = STp->buffer->buffer_size; - - /* write zero fm then read pos - if shows write error, try to recover - if no progress, wait */ - - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = WRITE_FILEMARKS; - cmd[1] = 1; - SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, DMA_NONE, STp->timeout, - MAX_RETRIES, 1); - - while (retval && time_before (jiffies, startwait + 5*60*HZ)) { - - if (STp->buffer->syscall_result && (SRpnt->sense[2] & 0x0f) != 2) { - - /* some failure - not just not-ready */ - retval = osst_write_error_recovery(STp, aSRpnt, 0); - break; - } - schedule_timeout_interruptible(HZ / OSST_POLL_PER_SEC); - - STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24; - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = READ_POSITION; - - SRpnt = osst_do_scsi(SRpnt, STp, cmd, 20, DMA_FROM_DEVICE, STp->timeout, - MAX_RETRIES, 1); - - retval = ( STp->buffer->syscall_result || (STp->buffer)->b_data[15] > 25 ); - STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize; - } - if (retval) - printk(KERN_ERR "%s:E: Device did not succeed to write buffered data\n", name); - } else - /* TODO - figure out which error conditions can be handled */ - if (STp->buffer->syscall_result) - printk(KERN_WARNING - "%s:W: Recover_wait_frame(read) cannot handle %02x:%02x:%02x\n", name, - (*aSRpnt)->sense[ 2] & 0x0f, - (*aSRpnt)->sense[12], - (*aSRpnt)->sense[13]); - - return retval; -} - -/* - * Read the next OnStream tape frame at the current location - */ -static int osst_read_frame(struct osst_tape * STp, struct osst_request ** aSRpnt, int timeout) -{ - unsigned char cmd[MAX_COMMAND_SIZE]; - struct osst_request * SRpnt; - int retval = 0; -#if DEBUG - os_aux_t * aux = STp->buffer->aux; - char * name = tape_name(STp); -#endif - - if (STp->poll) - if (osst_wait_frame (STp, aSRpnt, STp->first_frame_position, 0, timeout)) - retval = osst_recover_wait_frame(STp, aSRpnt, 0); - - memset(cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = READ_6; - cmd[1] = 1; - cmd[4] = 1; - -#if DEBUG - if (debugging) - printk(OSST_DEB_MSG "%s:D: Reading frame from OnStream tape\n", name); -#endif - SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, DMA_FROM_DEVICE, - STp->timeout, MAX_RETRIES, 1); - *aSRpnt = SRpnt; - if (!SRpnt) |