?? st.c
字號:
/* SCSI Tape Driver for Linux Version 0.02 for Linux 0.98.4 and Eric Youngdale's new scsi driver History: Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara. Contribution and ideas from several people including Eric Youngdale and Wolfgang Denk. Features: - support for different block sizes and internal buffering - support for fixed and variable block size (within buffer limit; blocksize set to zero) - *nix-style ioctl with codes from mtio.h from the QIC-02 driver by Hennus Bergman (command MTSETBLK added) - character device - rewind and non-rewind devices - capability to handle several tape drives simultaneously - one buffer if one drive, two buffers if more than one drive (limits the number of simultaneously open drives to two) - write behind - seek and tell (Tandberg compatible and SCSI-2) Devices: Autorewind devices have minor numbers equal to the tape numbers (0 > ). Nonrewind device has the minor number equal to tape number + 128. Problems: The end of media detection works correctly in writing only if the drive writes the buffer contents after the early-warning mark. If you want to be sure that EOM is reported correctly, you should uncomment the line defining ST_NO_DELAYED_WRITES. Note that when delayed writes are disabled each write byte count must be an integral number of blocks. Copyright 1992, 1993 Kai Makisara email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi Last modified: Thu Nov 25 21:49:02 1993 by root@kai.home*/#include <linux/fs.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/mtio.h>#include <linux/ioctl.h>#include <linux/fcntl.h>#include <asm/segment.h>#include <asm/system.h>#define MAJOR_NR SCSI_TAPE_MAJOR#include "../block/blk.h"#include "scsi.h"#include "scsi_ioctl.h"#include "st.h"#include "constants.h"/* Uncomment the following if you want the rewind, etc. commands return before command completion. *//* #define ST_NOWAIT *//* Uncomment the following if you want the tape to be positioned correctly within file after close (the tape is positioned correctly with respect to the filemarks even wihout ST_IN_FILE_POS defined *//* #define ST_IN_FILE_POS *//* Uncomment the following if you want recovered write errors to be fatal. *//* #define ST_RECOVERED_WRITE_FATAL *//* Uncomment the following if you want all data from a write command to be written to tape before the command returns. Disables write-behind. *//* #define ST_NO_DELAYED_WRITES *//* Number of ST_BLOCK_SIZE blocks in the buffers */#define ST_BUFFER_BLOCKS 64/* Write-behind can be disabled by setting ST_WRITE_THRESHOLD_BLOCKS equal to or larger than ST_BUFFER_BLOCKS */#define ST_WRITE_THRESHOLD_BLOCKS 60#define ST_BLOCK_SIZE 512#define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE)#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE)#ifdef ST_NO_DELAYED_WRITES#undef ST_WRITE_THRESHOLD_BLOCKS#define ST_WRITE_THRESHOLD_BLOCKS ST_BUFFER_BLOCKS#endif/* The buffer size should fit into the 24 bits reserved for length in the 6-byte SCSI read and write commands. */#if ST_BUFFER_SIZE >= (2 << 24 - 1)#error "Buffer size should not exceed (2 << 24 - 1) bytes!"#endif/* #define DEBUG */#define MAX_RETRIES 0#define MAX_READY_RETRIES 5#define NO_TAPE NOT_READY#define ST_TIMEOUT 9000#define ST_LONG_TIMEOUT 200000static int st_nbr_buffers;static ST_buffer *st_buffers[2];static Scsi_Tape * scsi_tapes;int NR_ST=0;int MAX_ST=0;static int st_int_ioctl(struct inode * inode,struct file * file, unsigned int cmd_in, unsigned long arg);/* Convert the result to success code */ static intst_chk_result(Scsi_Cmnd * SCpnt){ int dev = SCpnt->request.dev; int result = SCpnt->result; unsigned char * sense = SCpnt->sense_buffer; char *stp; if (!result && SCpnt->sense_buffer[0] == 0) return 0;#ifdef DEBUG printk("st%d: Error: %x\n", dev, result); print_sense("st", SCpnt);#endif/* if ((sense[0] & 0x70) == 0x70 && ((sense[2] & 0x80) )) return 0; */ if ((sense[0] & 0x70) == 0x70 && sense[2] == RECOVERED_ERROR#ifdef ST_RECOVERED_WRITE_FATAL && SCpnt->cmnd[0] != WRITE_6 && SCpnt->cmnd[0] != WRITE_FILEMARKS#endif ) { scsi_tapes[dev].recover_count++; if (SCpnt->cmnd[0] == READ_6) stp = "read"; else if (SCpnt->cmnd[0] == WRITE_6) stp = "write"; else stp = "ioctl"; printk("st%d: Recovered %s error (%d).\n", dev, stp, scsi_tapes[dev].recover_count); return 0; } return (-EIO);}/* Wakeup from interrupt */ static voidst_sleep_done (Scsi_Cmnd * SCpnt){ int st_nbr, remainder; Scsi_Tape * STp; if ((st_nbr = SCpnt->request.dev) < NR_ST && st_nbr >= 0) { STp = &(scsi_tapes[st_nbr]); if ((STp->buffer)->writing && (SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40)) { /* EOM at write-behind, has all been written? */ if ((SCpnt->sense_buffer[0] & 0x80) != 0) remainder = (SCpnt->sense_buffer[3] << 24) | (SCpnt->sense_buffer[4] << 16) | (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; else remainder = 0; if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW || remainder > 0) (STp->buffer)->last_result = SCpnt->result; /* Error */ else (STp->buffer)->last_result = INT_MAX; /* OK */ } else (STp->buffer)->last_result = SCpnt->result; (STp->buffer)->last_result_fatal = st_chk_result(SCpnt); if ((STp->buffer)->writing) SCpnt->request.dev = -1; else SCpnt->request.dev = 0xffff; if ((STp->buffer)->writing <= 0) wake_up( &(STp->waiting) ); }#ifdef DEBUG else printk("st?: Illegal interrupt device %x\n", st_nbr);#endif}#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS/* Handle the write-behind checking */ static voidwrite_behind_check(int dev){ Scsi_Tape * STp; ST_buffer * STbuffer; STp = &(scsi_tapes[dev]); STbuffer = STp->buffer; cli(); if (STbuffer->last_result < 0) { STbuffer->writing = (- STbuffer->writing); sleep_on( &(STp->waiting) ); STbuffer->writing = (- STbuffer->writing); } sti(); if (STbuffer->writing < STbuffer->buffer_bytes) memcpy(STbuffer->b_data, STbuffer->b_data + STbuffer->writing, STbuffer->buffer_bytes - STbuffer->writing); STbuffer->buffer_bytes -= STbuffer->writing; STbuffer->writing = 0; return;}#endif/* Flush the write buffer (never need to write if variable blocksize). */ static intflush_write_buffer(int dev){ int offset, transfer, blks; int result; unsigned char cmd[10]; Scsi_Cmnd *SCpnt; Scsi_Tape *STp = &(scsi_tapes[dev]);#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS if ((STp->buffer)->writing) { write_behind_check(dev); if ((STp->buffer)->last_result_fatal) {#ifdef DEBUG printk("st%d: Async write error %x.\n", dev, (STp->buffer)->last_result);#endif if ((STp->buffer)->last_result == INT_MAX) return (-ENOSPC); return (-EIO); } }#endif result = 0; if (STp->dirty == 1) { SCpnt = allocate_device(NULL, (STp->device)->index, 1); offset = (STp->buffer)->buffer_bytes; transfer = ((offset + STp->block_size - 1) / STp->block_size) * STp->block_size;#ifdef DEBUG printk("st%d: Flushing %d bytes.\n", dev, transfer);#endif memset((STp->buffer)->b_data + offset, 0, transfer - offset); SCpnt->sense_buffer[0] = 0; memset(cmd, 0, 10); cmd[0] = WRITE_6; cmd[1] = 1; blks = transfer / STp->block_size; cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; SCpnt->request.dev = dev; scsi_do_cmd (SCpnt, (void *) cmd, (STp->buffer)->b_data, transfer, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((STp->buffer)->last_result_fatal != 0) { printk("st%d: Error on flush.\n", dev); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40) && (SCpnt->sense_buffer[2] & 0x0f) != VOLUME_OVERFLOW) { STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; result = (-ENOSPC); } else result = (-EIO); } else { STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; } SCpnt->request.dev = -1; /* Mark as not busy */ } return result;}/* Flush the tape buffer. The tape will be positioned correctly unless seek_next is true. */ static intflush_buffer(struct inode * inode, struct file * filp, int seek_next){ int dev; int backspace, result; Scsi_Tape * STp; ST_buffer * STbuffer; dev = MINOR(inode->i_rdev) & 127; STp = &(scsi_tapes[dev]); STbuffer = STp->buffer; if (STp->rw == ST_WRITING) /* Writing */ return flush_write_buffer(dev); 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 ((STp->eof == ST_FM) && !STp->eof_hit) { result = st_int_ioctl(inode, filp, MTBSF, 1); /* Back over the EOF hit */ if (!result) { STp->eof = ST_NOEOF; STp->eof_hit = 0; } } if (!result && backspace > 0) result = st_int_ioctl(inode, filp, MTBSR, backspace); } return result;}/* Open the device */ static intscsi_tape_open(struct inode * inode, struct file * filp){ int dev; unsigned short flags; int i; unsigned char cmd[10]; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; dev = MINOR(inode->i_rdev) & 127; if (dev >= NR_ST) return (-ENODEV); STp = &(scsi_tapes[dev]); if (STp->in_use) { printk("st%d: Device already in use.\n", dev); return (-EBUSY); } /* Allocate buffer for this user */ for (i=0; i < st_nbr_buffers; i++) if (!st_buffers[i]->in_use) break; if (i >= st_nbr_buffers) { printk("st%d: No free buffers.\n", dev); return (-EBUSY); } STp->buffer = st_buffers[i]; (STp->buffer)->in_use = 1; (STp->buffer)->writing = 0; STp->in_use = 1; flags = filp->f_flags; STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY); STp->dirty = 0; STp->rw = ST_IDLE; STp->eof = ST_NOEOF; STp->eof_hit = 0; STp->recover_count = 0; SCpnt = allocate_device(NULL, (STp->device)->index, 1); if (!SCpnt) { printk("st%d: Tape request not allocated", dev); return (-EBUSY); } SCpnt->sense_buffer[0]=0; memset ((void *) &cmd[0], 0, 10); cmd[0] = TEST_UNIT_READY; SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ SCpnt->sense_buffer[0]=0; memset ((void *) &cmd[0], 0, 10); cmd[0] = TEST_UNIT_READY; SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); } if ((STp->buffer)->last_result_fatal != 0) { if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) printk("st%d: No tape.\n", dev); else printk("st%d: Error %x.\n", dev, SCpnt->result); (STp->buffer)->in_use = 0; STp->in_use = 0; SCpnt->request.dev = -1; /* Mark as not busy */ return (-EIO); } SCpnt->sense_buffer[0]=0; memset ((void *) &cmd[0], 0, 10); cmd[0] = READ_BLOCK_LIMITS; SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if (!SCpnt->result && !SCpnt->sense_buffer[0]) { STp->max_block = ((STp->buffer)->b_data[1] << 16) | ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3]; STp->min_block = ((STp->buffer)->b_data[4] << 8) | (STp->buffer)->b_data[5];#ifdef DEBUG printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block, STp->max_block);#endif } else { STp->min_block = STp->max_block = (-1);#ifdef DEBUG printk("st%d: Can't read block limits.\n", dev);#endif } SCpnt->sense_buffer[0]=0; memset ((void *) &cmd[0], 0, 10); cmd[0] = MODE_SENSE; cmd[4] = 12; SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((STp->buffer)->last_result_fatal != 0) {#ifdef DEBUG printk("st%d: No Mode Sense.\n", dev);#endif (STp->buffer)->b_data[2] = (STp->buffer)->b_data[3] = 0; } SCpnt->request.dev = -1; /* Mark as not busy */#ifdef DEBUG printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev, (STp->buffer)->b_data[0], (STp->buffer)->b_data[1], (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]);#endif if ((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];#ifdef DEBUG
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -