📄 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. Features: - support for different block sizes and internal buffering - *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 may not work correctly because of the buffering. If you want to do multiple tape backups relying on end of tape detection, you should disable write behind and in addition to that check that the tapes are readable. Kai Makisara, Nov 9, 1992 email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi Last changes Dec 6, 1992.*/#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 9#include "../blk.h"#include "scsi.h"#include "st.h"#define MAX_RETRIES 5#define NO_TAPE NOT_READY/* Uncomment the following if you want the rewind, etc. commands return before command completion. *//* #define ST_NOWAIT *//* #define DEBUG */#define ST_TIMEOUT 2000#define ST_LONG_TIMEOUT 200000/* 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)static 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 int arg);/* Wakeup from interrupt */static void st_sleep_done (Scsi_Cmnd * SCpnt){ int st_nbr; if ((st_nbr = SCpnt->request.dev) < NR_ST && st_nbr >= 0) { if (scsi_tapes[st_nbr].buffer->writing && (SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40)) scsi_tapes[st_nbr].buffer->last_result = 0x7fffffff; else scsi_tapes[st_nbr].buffer->last_result = SCpnt->result; if (scsi_tapes[st_nbr].buffer->writing) SCpnt->request.dev = -1; else SCpnt->request.dev = 0xffff; if (scsi_tapes[st_nbr].buffer->writing <= 0) wake_up( &scsi_tapes[st_nbr].waiting ); }#ifdef DEBUG else printk("st?: Illegal interrupt device %x\n", st_nbr);#endif}#ifdef DEBUG/* Print sense information */static void decode_sns(int dev, char *sense_buffer){ static char *snstext[] = { "None","Recovered Error","Not Ready","Medium Error","Hardware Error", "Illegal Request","Unit Attention","Data Protect","Blank Check", "Key=E","Key=F","Filemark","End-Of-Medium","Incorrect Block Length", "14","15"}; if (sense_buffer[0]!=0) { if ((sense_buffer[0] & 0x70) == 0x70) { if (sense_buffer[2] & 0x80) printk( "FMK "); if (sense_buffer[2] & 0x40) printk( "EOM "); if (sense_buffer[2] & 0x20) printk( "ILI "); printk( "st%d: sense key %s\n", dev, snstext[sense_buffer[2] & 0x0f]); } else { if (sense_buffer[0] < 15) printk("st%d: old sense key %s\n", dev, snstext[sense_buffer[0] & 0x0f]); else printk("st%d: sns = %2x %2x\n", dev, sense_buffer[0], sense_buffer[2]); } } return;}#endif/* Convert the result to success code */static int st_chk_result(int dev, int result, char *sense){ if (!result) return 0;#ifdef DEBUG printk("st%d: Error: %x\n", dev, result); decode_sns(dev, sense);#endif if ((sense[0] & 0x70) == 0x70 && ((sense[2] & 0x80) /* || ((sense[2] & 0x0f) == 8) */ )) return 0; return (-EIO);}#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS/* Handle the write-behind checking */static void write_behind_check(int dev){ cli(); if (scsi_tapes[dev].buffer->last_result < 0) { scsi_tapes[dev].buffer->writing = (- scsi_tapes[dev].buffer->writing); sleep_on( &scsi_tapes[dev].waiting ); scsi_tapes[dev].buffer->writing = (- scsi_tapes[dev].buffer->writing); } sti(); if (scsi_tapes[dev].buffer->writing < scsi_tapes[dev].buffer->buffer_bytes) memcpy(scsi_tapes[dev].buffer->b_data, scsi_tapes[dev].buffer->b_data + scsi_tapes[dev].buffer->writing, scsi_tapes[dev].buffer->buffer_bytes - scsi_tapes[dev].buffer->writing); scsi_tapes[dev].buffer->buffer_bytes -= scsi_tapes[dev].buffer->writing; scsi_tapes[dev].buffer->writing = 0; return;}#endif/* Flush the write buffer */static int flush_write_buffer(int dev){ int offset, transfer, blks; int result; unsigned char cmd[10]; Scsi_Cmnd *SCpnt;#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS if (scsi_tapes[dev].buffer->writing) { write_behind_check(dev); if (scsi_tapes[dev].buffer->last_result) {#ifdef DEBUG printk("st%d: Async write error %x.\n", dev, scsi_tapes[dev].buffer->last_result);#endif return (-EIO); } }#endif result = 0; if (scsi_tapes[dev].dirty==1) { SCpnt = allocate_device(NULL, scsi_tapes[dev].device->index, 1); offset = scsi_tapes[dev].buffer->buffer_bytes; transfer = ((offset + scsi_tapes[dev].block_size - 1) / scsi_tapes[dev].block_size) * scsi_tapes[dev].block_size;#ifdef DEBUG printk("st%d: Flushing %d bytes.\n", dev, transfer);#endif memset(scsi_tapes[dev].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 / scsi_tapes[dev].block_size; cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; SCpnt->request.dev = dev; scsi_do_cmd (SCpnt, (void *) cmd, scsi_tapes[dev].buffer->b_data, transfer, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); if (SCpnt->result != 0) { printk("st%d: Error on flush:\n", dev);#ifdef DEBUG st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);#endif result = (-EIO); } else { scsi_tapes[dev].dirty = 0; scsi_tapes[dev].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 int flush_buffer(struct inode * inode, struct file * filp, int seek_next){ int dev; int backspace, result; dev = inode->i_rdev & 127; if (scsi_tapes[dev].rw == 2) /* Writing */ return flush_write_buffer(dev); backspace = (scsi_tapes[dev].buffer->buffer_bytes + scsi_tapes[dev].buffer->read_pointer) / scsi_tapes[dev].block_size - (scsi_tapes[dev].buffer->read_pointer + scsi_tapes[dev].block_size - 1) / scsi_tapes[dev].block_size; scsi_tapes[dev].buffer->buffer_bytes = 0; scsi_tapes[dev].buffer->read_pointer = 0; result = 0; if (!seek_next && backspace > 0) { result = st_int_ioctl(inode, filp, MTBSR, backspace); if (!result) { scsi_tapes[dev].eof = 0; scsi_tapes[dev].eof_hit = 0; } } return result;}/* Open the device */static int scsi_tape_open(struct inode * inode, struct file * filp){ int dev; unsigned short flags; int i; unsigned char cmd[10]; Scsi_Cmnd * SCpnt; dev = inode->i_rdev & 127; if (dev >= NR_ST) return (-ENXIO); if (scsi_tapes[dev].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); } st_buffers[i]->in_use = 1; st_buffers[i]->writing = 0; scsi_tapes[dev].buffer = st_buffers[i]; scsi_tapes[dev].in_use = 1; flags = filp->f_flags; scsi_tapes[dev].write_prot = ((flags & O_ACCMODE) == O_RDONLY); scsi_tapes[dev].dirty = 0; scsi_tapes[dev].rw = 0; scsi_tapes[dev].eof = 0; scsi_tapes[dev].eof_hit = 0; SCpnt = allocate_device(NULL, scsi_tapes[dev].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 *) scsi_tapes[dev].buffer->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */#ifdef DEBUG decode_sns(dev, SCpnt->sense_buffer);#endif 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 *) scsi_tapes[dev].buffer->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); } if (SCpnt->result != 0) {#ifdef DEBUG decode_sns(dev, SCpnt->sense_buffer);#endif 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); scsi_tapes[dev].buffer->in_use = 0; scsi_tapes[dev].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 *) scsi_tapes[dev].buffer->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); if (!SCpnt->result && !SCpnt->sense_buffer[0]) { scsi_tapes[dev].max_block = (scsi_tapes[dev].buffer->b_data[1] << 16) | (scsi_tapes[dev].buffer->b_data[2] << 8) | scsi_tapes[dev].buffer->b_data[3]; scsi_tapes[dev].min_block = (scsi_tapes[dev].buffer->b_data[4] << 8) | scsi_tapes[dev].buffer->b_data[5];#ifdef DEBUG printk("st%d: Block limits %d - %d bytes.\n", dev, scsi_tapes[dev].min_block, scsi_tapes[dev].max_block);#endif } else { scsi_tapes[dev].min_block = scsi_tapes[dev].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 *) scsi_tapes[dev].buffer->b_data, ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting ); i = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer); if (i) {#ifdef DEBUG printk("st%d: No Mode Sense.\n", dev);#endif scsi_tapes[dev].buffer->b_data[2] = scsi_tapes[dev].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, scsi_tapes[dev].buffer->b_data[0], scsi_tapes[dev].buffer->b_data[1], scsi_tapes[dev].buffer->b_data[2], scsi_tapes[dev].buffer->b_data[3]);#endif if (scsi_tapes[dev].buffer->b_data[3] >= 8) { scsi_tapes[dev].block_size = scsi_tapes[dev].buffer->b_data[9] * 65536 +
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -