extractedLnx/linux-2.2.26/drivers/scsi/st.c_st_int_ioctl.c
/* Internal ioctl function */
static int
st_int_ioctl(struct inode * inode,
unsigned int cmd_in, unsigned long arg)
{
int timeout;
long ltmp;
int i, ioctl_result;
int chg_eof = TRUE;
unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
ST_partstat * STps;
int fileno, blkno, at_sm, undone, datalen;
int dev = TAPE_NR(inode->i_rdev);
STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY && cmd_in != MTLOAD) {
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, 10);
datalen = 0;
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;
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Spacing tape forward over %d filemarks.\n",
dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif
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;
#if DEBUG
if (debugging) {
if (cmd[2] & 0x80)
ltmp = 0xff000000;
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk(ST_DEB_MSG "st%d: Spacing tape backward over %ld filemarks.\n",
dev, (-ltmp));
}
#endif
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;
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Spacing tape forward %d blocks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif
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;
#if DEBUG
if (debugging) {
if (cmd[2] & 0x80)
ltmp = 0xff000000;
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk(ST_DEB_MSG "st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp));
}
#endif
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;
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Spacing tape forward %d setmarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif
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;
#if DEBUG
if (debugging) {
if (cmd[2] & 0x80)
ltmp = 0xff000000;
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk(ST_DEB_MSG "st%d: Spacing tape backward %ld setmarks.\n",
dev, (-ltmp));
}
#endif
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;
#if DEBUG
if (debugging) {
if (cmd_in == MTWEOF)
printk(ST_DEB_MSG "st%d: Writing %d filemarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
else
printk(ST_DEB_MSG "st%d: Writing %d setmarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
}
#endif
if (fileno >= 0)
fileno += arg;
blkno = 0;
at_sm = (cmd_in == MTWSM);
break;
case MTREW:
cmd[0] = REZERO_UNIT;
#if ST_NOWAIT
cmd[1] = 1; /* Don't wait for completion */
timeout = STp->timeout;
#endif
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Rewinding tape.\n", dev);
#endif
fileno = blkno = at_sm = 0 ;
break;
case MTOFFL:
case MTLOAD:
case MTUNLOAD:
cmd[0] = START_STOP;
if (cmd_in == MTLOAD)
cmd[4] |= 1;
/*
* If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A
*/
if (cmd_in != MTOFFL &&
arg >= 1 + MT_ST_HPLOADER_OFFSET
&& arg <= 6 + MT_ST_HPLOADER_OFFSET) {
#if DEBUG
if (debugging) {
printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n",
dev, (cmd[4]) ? "" : "un",
arg - MT_ST_HPLOADER_OFFSET);
}
#endif
cmd[3] = arg - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
}
#if ST_NOWAIT
cmd[1] = 1; /* Don't wait for completion */
timeout = STp->timeout;
#else
timeout = STp->long_timeout;
#endif
#if DEBUG
if (debugging) {
if (cmd_in != MTLOAD)
printk(ST_DEB_MSG "st%d: Unloading tape.\n", dev);
else
printk(ST_DEB_MSG "st%d: Loading tape.\n", dev);
}
#endif
fileno = blkno = at_sm = 0 ;
break;
case MTNOP:
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: No op on tape.\n", dev);
#endif
return 0; /* Should do something ? */
break;
case MTRETEN:
cmd[0] = START_STOP;
#if ST_NOWAIT
cmd[1] = 1; /* Don't wait for completion */
timeout = STp->timeout;
#endif
cmd[4] = 3;
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Retensioning tape.\n", dev);
#endif
fileno = blkno = at_sm = 0;
break;
case MTEOM:
if (!STp->fast_mteom) {
/* space to the end of tape */
ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
fileno = STps->drv_file;
if (STps->eof >= ST_EOD_1)
return 0;
/* The next lines would hide the number of spaced FileMarks
That's why I inserted the previous lines. I had no luck
with detecting EOM with FSF, so we go now to EOM.
Joerg Weule */
}
else
fileno = (-1);
cmd[0] = SPACE;
cmd[1] = 3;
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Spacing to end of recorded medium.\n", dev);
#endif
blkno = 0;
at_sm = 0;
break;
case MTERASE:
if (STp->write_prot)
return (-EACCES);
cmd[0] = ERASE;
cmd[1] = 1; /* To the end of tape */
#if ST_NOWAIT
cmd[1] |= 2; /* Don't wait for completion */
timeout = STp->timeout;
#else
timeout = STp->long_timeout * 8;
#endif
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Erasing tape.\n", dev);
#endif
fileno = blkno = at_sm = 0 ;
break;
case MTLOCK:
chg_eof = FALSE;
cmd[0] = ALLOW_MEDIUM_REMOVAL;
cmd[4] = SCSI_REMOVAL_PREVENT;
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Locking drive door.\n", dev);
#endif;
break;
case MTUNLOCK:
chg_eof = FALSE;
cmd[0] = ALLOW_MEDIUM_REMOVAL;
cmd[4] = SCSI_REMOVAL_ALLOW;
#if DEBUG
if (debugging)
printk(ST_DEB_MSG "st%d: Unlocking drive door.\n", dev);
#endif;
break;
case MTSETBLK: /* Set block length */
case MTSETDENSITY: /* Set tape density */
case MTSETDRVBUFFER: /* Set drive buffering */
case SET_DENS_AND_BLK: /* Set density and block size */
chg_eof = FALSE;
if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
return (-EIO); /* Not allowed if data in buffer */
if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
(arg & MT_ST_BLKSIZE_MASK) != 0 &&
((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
(arg & MT_ST_BLKSIZE_MASK) > STp->max_block ||
(arg & MT_ST_BLKSIZE_MASK) > st_buffer_size)) {
printk(KERN_WARNING "st%d: Illegal block size.\n", dev);
return (-EINVAL);
}
cmd[0] = MODE_SELECT;
if ((STp->use_pf & USE_PF))
cmd[1] = 0x10;
cmd[4] = datalen = 12;
memset((STp->buffer)->b_data, 0, 12);
if (cmd_in == MTSETDRVBUFFER)
(STp->buffer)->b_data[2] = (arg & 7) << 4;
else
(STp->buffer)->b_data[2] =
STp->drv_buffer << 4;
(STp->buffer)->b_data[3] = 8; /* block descriptor length */
if (cmd_in == MTSETDENSITY) {
(STp->buffer)->b_data[4] = arg;
STp->density_changed = TRUE; /* At least we tried ;-) */
}
else if (cmd_in == SET_DENS_AND_BLK)
(STp->buffer)->b_data[4] = arg >> 24;
else
(STp->buffer)->b_data[4] = STp->density;
if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
ltmp = arg & MT_ST_BLKSIZE_MASK;
if (cmd_in == MTSETBLK)
STp->blksize_changed = TRUE; /* At least we tried ;-) */
}
else
ltmp = STp->block_size;
(STp->buffer)->b_data[9] = (ltmp >> 16);
(STp->buffer)->b_data[10] = (ltmp >> 8);
(STp->buffer)->b_data[11] = ltmp;
timeout = STp->timeout;
#if DEBUG
if (debugging) {
if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
printk(ST_DEB_MSG "st%d: Setting block size to %d bytes.\n", dev,
(STp->buffer)->b_data[9] * 65536 +
(STp->buffer)->b_data[10] * 256 +
(STp->buffer)->b_data[11]);
if (cmd_in == MTSETDENSITY || cmd_in == SET_DENS_AND_BLK)
printk(ST_DEB_MSG "st%d: Setting density code to %x.\n", dev,
(STp->buffer)->b_data[4]);
if (cmd_in == MTSETDRVBUFFER)
printk(ST_DEB_MSG "st%d: Setting drive buffer code to %d.\n", dev,
((STp->buffer)->b_data[2] >> 4) & 7);
}
#endif
break;
default:
return (-ENOSYS);
}
SCpnt = st_do_scsi(NULL, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE);
if (!SCpnt)
return (-EBUSY);
ioctl_result = (STp->buffer)->last_result_fatal;
if (!ioctl_result) { /* SCSI command successful */
scsi_release_command(SCpnt);
SCpnt = NULL;
STps->drv_block = blkno;
STps->drv_file = fileno;
STps->at_sm = at_sm;
if (cmd_in == MTLOCK)
STp->door_locked = ST_LOCKED_EXPLICIT;
else if (cmd_in == MTUNLOCK)
STp->door_locked = ST_UNLOCKED;
if (cmd_in == MTBSFM)
ioctl_result = st_int_ioctl(inode, MTFSF, 1);
else if (cmd_in == MTFSFM)
ioctl_result = st_int_ioctl(inode, MTBSF, 1);
if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
STp->block_size = arg & MT_ST_BLKSIZE_MASK;
if (STp->block_size != 0)
(STp->buffer)->buffer_blocks =
(STp->buffer)->buffer_size / STp->block_size;
(STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
if (cmd_in == SET_DENS_AND_BLK)
STp->density = arg >> MT_ST_DENSITY_SHIFT;
}
else if (cmd_in == MTSETDRVBUFFER)
STp->drv_buffer = (arg & 7);
else if (cmd_in == MTSETDENSITY)
STp->density = arg;
if (cmd_in == MTEOM)
STps->eof = ST_EOD;
else if (cmd_in == MTFSF)
STps->eof = ST_FM;
else if (chg_eof)
STps->eof = ST_NOEOF;
if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
STp->rew_at_close = 0;
else if (cmd_in == MTLOAD) {
STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
for (i=0; i < ST_NBR_PARTITIONS; i++) {
STp->ps[i].rw = ST_IDLE;
STp->ps[i].last_block_valid = FALSE;
}
STp->partition = 0;
}
} else { /* SCSI command was not completely successful. Don't return
from this block without releasing the SCSI command block! */
if (SCpnt->sense_buffer[2] & 0x40) {
if (cmd_in != MTBSF && cmd_in != MTBSFM &&
cmd_in != MTBSR && cmd_in != MTBSS)
STps->eof = ST_EOM_OK;
STps->drv_block = 0;
}
undone = (
(SCpnt->sense_buffer[3] << 24) +
(SCpnt->sense_buffer[4] << 16) +
(SCpnt->sense_buffer[5] << 8) +
SCpnt->sense_buffer[6] );
if (cmd_in == MTWEOF &&
(SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x4f) == 0x40 &&
((SCpnt->sense_buffer[0] & 0x80) == 0 || undone == 0)) {
ioctl_result = 0; /* EOF written succesfully at EOM */
if (fileno >= 0)
fileno++;
STps->drv_file = fileno;
STps->eof = ST_NOEOF;
}
else if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
if (fileno >= 0)
STps->drv_file = fileno - undone ;
else
STps->drv_file = fileno;
STps->drv_block = 0;
STps->eof = ST_NOEOF;
}
else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
if (STps->drv_file >= 0)
STps->drv_file = fileno + undone ;
else
STps->drv_file = fileno;
STps->drv_block = 0;
STps->eof = ST_NOEOF;
}
else if (cmd_in == MTFSR) {
if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
if (STps->drv_file >= 0)
STps->drv_file++;
STps->drv_block = 0;
STps->eof = ST_FM;
}
else {
if (blkno >= undone)
STps->drv_block = blkno - undone;
else
STps->drv_block = (-1);
STps->eof = ST_NOEOF;
}
}
else if (cmd_in == MTBSR) {
if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
STps->drv_file--;
STps->drv_block = (-1);
}
else {
if (STps->drv_block >= 0)
STps->drv_block = blkno + undone;
else
STps->drv_block = (-1);
}
STps->eof = ST_NOEOF;
}
else if (cmd_in == MTEOM) {
STps->drv_file = (-1);
STps->drv_block = (-1);
STps->eof = ST_EOD;
}
else if (cmd_in == MTSETBLK ||
cmd_in == MTSETDENSITY ||
cmd_in == MTSETDRVBUFFER ||
cmd_in == SET_DENS_AND_BLK) {
if ((SCpnt->sense_buffer[2] & 0x0f) == ILLEGAL_REQUEST &&
!(STp->use_pf & PF_TESTED)) {
/* Try the other possible state of Page Format if not
already tried */
STp->use_pf = !STp->use_pf | PF_TESTED;
scsi_release_command(SCpnt);
SCpnt = NULL;
return st_int_ioctl(inode, cmd_in, arg);
}
}
else if (chg_eof)
STps->eof = ST_NOEOF;
if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
STps->eof = ST_EOD;
if (cmd_in == MTLOCK)
STp->door_locked = ST_LOCK_FAILS;
scsi_release_command(SCpnt);
SCpnt = NULL;
}
return ioctl_result;
}
Generated by GNU enscript 1.6.4.