|
|
GNU Mach 1.0
/*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
* File: rz_tape.c
* Author: Alessandro Forin, Carnegie Mellon University
* Date: 10/90
*
* Top layer of the SCSI driver: interface with the MI.
* This file contains operations specific to TAPE-like devices.
*/
#include <mach/std_types.h>
#include <scsi/compat_30.h>
#include <sys/ioctl.h>
#ifdef MACH_KERNEL
#include <device/tape_status.h>
#else /*MACH_KERNEL*/
#include <mips/PMAX/tape_status.h>
#endif /*MACH_KERNEL*/
#include <scsi/scsi.h>
#include <scsi/scsi_defs.h>
#include <scsi/rz.h>
#if (NSCSI > 0)
void sctape_start(); /* forward */
int scsi_tape_timeout = 5*60; /* secs, tk50 is slow when positioning far apart */
int sctape_open(tgt, req)
target_info_t *tgt;
io_req_t req;
{
io_return_t ret;
io_req_t ior;
int i;
scsi_mode_sense_data_t *mod;
#ifdef MACH_KERNEL
req->io_device->flag |= D_EXCL_OPEN;
#endif /*MACH_KERNEL*/
/* Preferably allow tapes to disconnect */
if (BGET(scsi_might_disconnect,(unsigned char)tgt->masterno,tgt->target_id))
BSET(scsi_should_disconnect,(unsigned char)tgt->masterno,tgt->target_id);
/*
* Dummy ior for proper sync purposes
*/
io_req_alloc(ior,0);
ior->io_count = 0;
/*
* Do a mode sense first, some drives might be picky
* about changing params [even if the standard might
* say otherwise, sigh.]
*/
do {
ior->io_op = IO_INTERNAL;
ior->io_next = 0;
ior->io_error = 0;
ret = scsi_mode_sense(tgt, 0, 32, ior);
} while (ret == SCSI_RET_RETRY);
mod = (scsi_mode_sense_data_t *)tgt->cmd_ptr;
if (scsi_debug) {
int p[5];
bcopy((char*)mod, (char*)p, sizeof(p));
printf("[modsns(%x): x%x x%x x%x x%x x%x]", ret,
p[0], p[1], p[2], p[3], p[4]);
}
if (ret == SCSI_RET_DEVICE_DOWN)
goto out;
if (ret == SCSI_RET_SUCCESS) {
tgt->dev_info.tape.read_only = mod->wp;
tgt->dev_info.tape.speed = mod->speed;
tgt->dev_info.tape.density = mod->bdesc[0].density_code;
} /* else they all default sensibly, using zeroes */
/* Some tapes have limits on record-length */
again:
ior->io_op = IO_INTERNAL;
ior->io_next = 0;
ior->io_error = 0;
ret = scsi_read_block_limits( tgt, ior);
if (ret == SCSI_RET_RETRY) goto again;
if (!ior->io_error && (ret == SCSI_RET_SUCCESS)) {
scsi_blimits_data_t *lim;
int maxl;
lim = (scsi_blimits_data_t *) tgt->cmd_ptr;
tgt->block_size = (lim->minlen_msb << 8) |
lim->minlen_lsb;
maxl = (lim->maxlen_msb << 16) |
(lim->maxlen_sb << 8) |
lim->maxlen_lsb;
if (maxl == 0)
maxl = (unsigned)-1;
tgt->dev_info.tape.maxreclen = maxl;
tgt->dev_info.tape.fixed_size = (maxl == tgt->block_size);
} else {
/* let the user worry about it */
/* default: tgt->block_size = 1; */
tgt->dev_info.tape.maxreclen = (unsigned)-1;
tgt->dev_info.tape.fixed_size = FALSE;
}
/* Try hard to do a mode select */
for (i = 0; i < 5; i++) {
ior->io_op = IO_INTERNAL;
ior->io_error = 0;
ret = sctape_mode_select(tgt, 0, 0, FALSE, ior);
if (ret == SCSI_RET_SUCCESS)
break;
}
if (scsi_watchdog_period < scsi_tape_timeout)
scsi_watchdog_period += scsi_tape_timeout;
#if 0 /* this might imply rewind, which we do not want, although yes, .. */
/* we want the tape loaded */
ior->io_op = IO_INTERNAL;
ior->io_next = 0;
ior->io_error = 0;
ret = scsi_start_unit(tgt, SCSI_CMD_SS_START, ior);
#endif
req->io_device->bsize = tgt->block_size;
out:
io_req_free(ior);
return ret;
}
io_return_t sctape_close(tgt)
target_info_t *tgt;
{
io_return_t ret = SCSI_RET_SUCCESS;
io_req_t ior;
/*
* Dummy ior for proper sync purposes
*/
io_req_alloc(ior,0);
ior->io_op = IO_INTERNAL;
ior->io_next = 0;
ior->io_count = 0;
if (tgt->ior) printf("TAPE: Close with pending requests ?? \n");
/* write a filemark if we xtnded/truncated the tape */
if (tgt->flags & TGT_WRITTEN_TO) {
tgt->ior = ior;
ior->io_error = 0;
ret = scsi_write_filemarks(tgt, 2, ior);
if (ret != SCSI_RET_SUCCESS)
printf("%s%d: wfmark failed x%x\n",
(*tgt->dev_ops->driver_name)(TRUE), tgt->target_id, ret);
/*
* Don't bother repositioning if we'll rewind it
*/
if (tgt->flags & TGT_REWIND_ON_CLOSE)
goto rew;
retry:
tgt->ior = ior;
ior->io_op = IO_INTERNAL;
ior->io_error = 0;
ior->io_next = 0;
ret = scsi_space(tgt, SCSI_CMD_SP_FIL, -1, ior);
if (ret != SCSI_RET_SUCCESS) {
if (ret == SCSI_RET_RETRY) {
timeout(wakeup, tgt, hz);
await(tgt);
goto retry;
}
printf("%s%d: bspfile failed x%x\n",
(*tgt->dev_ops->driver_name)(TRUE), tgt->target_id, ret);
}
}
rew:
if (tgt->flags & TGT_REWIND_ON_CLOSE) {
/* Rewind tape */
ior->io_error = 0;
ior->io_op = IO_INTERNAL;
ior->io_error = 0;
tgt->ior = ior;
(void) scsi_rewind(tgt, ior, FALSE);
iowait(ior);
if (tgt->done == SCSI_RET_RETRY) {
timeout(wakeup, tgt, 5*hz);
await(tgt);
goto rew;
}
}
io_req_free(ior);
tgt->flags &= ~(TGT_ONLINE|TGT_WRITTEN_TO|TGT_REWIND_ON_CLOSE);
return ret;
}
int sctape_strategy(ior)
register io_req_t ior;
{
target_info_t *tgt;
register int i = ior->io_unit;
tgt = scsi_softc[rzcontroller(i)]->target[rzslave(i)];
if (((ior->io_op & IO_READ) == 0) &&
tgt->dev_info.tape.read_only) {
ior->io_error = D_INVALID_OPERATION;
ior->io_op |= IO_ERROR;
ior->io_residual = ior->io_count;
iodone(ior);
return ior->io_error;
}
return rz_simpleq_strategy( ior, sctape_start);
}
static void
do_residue(ior, sns, bsize)
io_req_t ior;
scsi_sense_data_t *sns;
int bsize;
{
int residue;
/* Not an error situation */
ior->io_error = 0;
ior->io_op &= ~IO_ERROR;
if (!sns->addr_valid) {
ior->io_residual = ior->io_count;
return;
}
residue = sns->u.xtended.info0 << 24 |
sns->u.xtended.info1 << 16 |
sns->u.xtended.info2 << 8 |
sns->u.xtended.info3;
/* fixed ? */
residue *= bsize;
/*
* NOTE: residue == requested - actual
* We only care if > 0
*/
if (residue < 0) residue = 0;/* sanity */
ior->io_residual += residue;
}
void sctape_start( tgt, done)
target_info_t *tgt;
boolean_t done;
{
io_req_t head, ior = tgt->ior;
if (ior == 0)
return;
if (done) {
/* see if we must retry */
if ((tgt->done == SCSI_RET_RETRY) &&
((ior->io_op & IO_INTERNAL) == 0)) {
delay(1000000);/*XXX*/
goto start;
} else
/* got a bus reset ? ouch, that hurts */
if (tgt->done == (SCSI_RET_ABORTED|SCSI_RET_RETRY)) {
/*
* we really cannot retry because the tape position
* is lost.
*/
printf("Lost tape position\n");
ior->io_error = D_IO_ERROR;
ior->io_op |= IO_ERROR;
} else
/* check completion status */
if (tgt->cur_cmd == SCSI_CMD_REQUEST_SENSE) {
scsi_sense_data_t *sns;
ior->io_op = ior->io_temporary;
ior->io_error = D_IO_ERROR;
ior->io_op |= IO_ERROR;
sns = (scsi_sense_data_t *)tgt->cmd_ptr;
if (scsi_debug)
scsi_print_sense_data(sns);
if (scsi_check_sense_data(tgt, sns)) {
if (sns->u.xtended.ili) {
if (ior->io_op & IO_READ) {
do_residue(ior, sns, tgt->block_size);
if (scsi_debug)
printf("Tape Short Read (%d)\n",
ior->io_residual);
}
} else if (sns->u.xtended.eom) {
do_residue(ior, sns, tgt->block_size);
if (scsi_debug)
printf("End of Physical Tape!\n");
} else if (sns->u.xtended.fm) {
do_residue(ior, sns, tgt->block_size);
if (scsi_debug)
printf("File Mark\n");
}
}
}
else if (tgt->done != SCSI_RET_SUCCESS) {
if (tgt->done == SCSI_RET_NEED_SENSE) {
ior->io_temporary = ior->io_op;
ior->io_op = IO_INTERNAL;
if (scsi_debug)
printf("[NeedSns x%x x%x]", ior->io_residual, ior->io_count);
scsi_request_sense(tgt, ior, 0);
return;
} else if (tgt->done == SCSI_RET_RETRY) {
/* only retry here READs and WRITEs */
if ((ior->io_op & IO_INTERNAL) == 0) {
ior->io_residual = 0;
goto start;
} else{
ior->io_error = D_WOULD_BLOCK;
ior->io_op |= IO_ERROR;
}
} else {
ior->io_error = D_IO_ERROR;
ior->io_op |= IO_ERROR;
}
}
if (scsi_debug)
printf("[Resid x%x]", ior->io_residual);
/* dequeue next one */
head = ior;
simple_lock(&tgt->target_lock);
ior = head->io_next;
tgt->ior = ior;
if (ior)
ior->io_prev = head->io_prev;
simple_unlock(&tgt->target_lock);
iodone(head);
if (ior == 0)
return;
}
ior->io_residual = 0;
start:
if (ior->io_op & IO_READ) {
tgt->flags &= ~TGT_WRITTEN_TO;
sctape_read( tgt, ior );
} else if ((ior->io_op & IO_INTERNAL) == 0) {
tgt->flags |= TGT_WRITTEN_TO;
sctape_write( tgt, ior );
}
}
io_return_t
sctape_get_status( dev, tgt, flavor, status, status_count)
int dev;
target_info_t *tgt;
dev_flavor_t flavor;
dev_status_t status;
natural_t *status_count;
{
switch (flavor) {
case DEV_GET_SIZE:
status[DEV_GET_SIZE_DEVICE_SIZE] = 0;
status[DEV_GET_SIZE_RECORD_SIZE] = tgt->block_size;
*status_count = DEV_GET_SIZE_COUNT;
break;
case TAPE_STATUS: {
struct tape_status *ts = (struct tape_status *) status;
ts->mt_type = MT_ISSCSI;
ts->speed = tgt->dev_info.tape.speed;
ts->density = tgt->dev_info.tape.density;
ts->flags = (tgt->flags & TGT_REWIND_ON_CLOSE) ?
TAPE_FLG_REWIND : 0;
if (tgt->dev_info.tape.read_only)
ts->flags |= TAPE_FLG_WP;
#ifdef MACH_KERNEL
*status_count = TAPE_STATUS_COUNT;
#endif
break;
}
/* U*x compat */
case MTIOCGET: {
struct mtget *g = (struct mtget *) status;
bzero(g, sizeof(struct mtget));
g->mt_type = 0x7; /* Ultrix compat */
#ifdef MACH_KERNEL
*status_count = sizeof(struct mtget)/sizeof(int);
#endif
break;
}
default:
return D_INVALID_OPERATION;
}
return D_SUCCESS;
}
io_return_t
sctape_set_status( dev, tgt, flavor, status, status_count)
int dev;
target_info_t *tgt;
dev_flavor_t flavor;
dev_status_t status;
natural_t status_count;
{
scsi_ret_t ret;
switch (flavor) {
case TAPE_STATUS: {
struct tape_status *ts = (struct tape_status *) status;
if (ts->flags & TAPE_FLG_REWIND)
tgt->flags |= TGT_REWIND_ON_CLOSE;
else
tgt->flags &= ~TGT_REWIND_ON_CLOSE;
if (ts->speed || ts->density) {
unsigned int ospeed, odensity;
io_req_t ior;
io_req_alloc(ior,0);
ior->io_op = IO_INTERNAL;
ior->io_error = 0;
ior->io_next = 0;
ior->io_count = 0;
ospeed = tgt->dev_info.tape.speed;
odensity = tgt->dev_info.tape.density;
tgt->dev_info.tape.speed = ts->speed;
tgt->dev_info.tape.density = ts->density;
ret = sctape_mode_select(tgt, 0, 0, (ospeed == ts->speed), ior);
if (ret != SCSI_RET_SUCCESS) {
tgt->dev_info.tape.speed = ospeed;
tgt->dev_info.tape.density = odensity;
}
io_req_free(ior);
}
break;
}
/* U*x compat */
case MTIOCTOP: {
struct tape_params *mt = (struct tape_params *) status;
io_req_t ior;
if (scsi_debug)
printf("[sctape_sstatus: %x %x %x]\n",
flavor, mt->mt_operation, mt->mt_repeat_count);
io_req_alloc(ior,0);
retry:
ior->io_count = 0;
ior->io_op = IO_INTERNAL;
ior->io_error = 0;
ior->io_next = 0;
tgt->ior = ior;
/* compat: in U*x it is a short */
switch ((short)(mt->mt_operation)) {
case MTWEOF: /* write an end-of-file record */
ret = scsi_write_filemarks(tgt, mt->mt_repeat_count, ior);
break;
case MTFSF: /* forward space file */
ret = scsi_space(tgt, SCSI_CMD_SP_FIL, mt->mt_repeat_count, ior);
break;
case MTBSF: /* backward space file */
ret = scsi_space(tgt, SCSI_CMD_SP_FIL, -mt->mt_repeat_count,ior);
break;
case MTFSR: /* forward space record */
ret = scsi_space(tgt, SCSI_CMD_SP_BLOCKS, mt->mt_repeat_count, ior);
break;
case MTBSR: /* backward space record */
ret = scsi_space(tgt, SCSI_CMD_SP_BLOCKS, -mt->mt_repeat_count, ior);
break;
case MTREW: /* rewind */
case MTOFFL: /* rewind and put the drive offline */
ret = scsi_rewind(tgt, ior, TRUE);
iowait(ior);
if ((short)(mt->mt_operation) == MTREW) break;
ior->io_op = 0;
ior->io_next = 0;
ior->io_error = 0;
(void) scsi_start_unit(tgt, 0, ior);
break;
case MTNOP: /* no operation, sets status only */
case MTCACHE: /* enable controller cache */
case MTNOCACHE: /* disable controller cache */
ret = SCSI_RET_SUCCESS;
break;
default:
tgt->ior = 0;
io_req_free(ior);
return D_INVALID_OPERATION;
}
if (ret == SCSI_RET_RETRY) {
timeout(wakeup, ior, 5*hz);
await(ior);
goto retry;
}
io_req_free(ior);
if (ret != SCSI_RET_SUCCESS)
return D_IO_ERROR;
break;
}
case MTIOCIEOT:
case MTIOCEEOT:
default:
return D_INVALID_OPERATION;
}
return D_SUCCESS;
}
#endif /* NSCSI > 0 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.