#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mscp_tape.c,v 1.44 2024/01/11 06:19:49 mrg Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/bufq.h>
#include <sys/ioccom.h>
#include <sys/mtio.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <dev/mscp/mscp.h>
#include <dev/mscp/mscpreg.h>
#include <dev/mscp/mscpvar.h>
#include "locators.h"
struct mt_softc {
device_t mt_dev;
int mt_state;
int mt_hwunit;
int mt_inuse;
int mt_waswrite;
int mt_serex;
int mt_ioctlerr;
};
#define MT_OFFLINE 0
#define MT_ONLINE 1
int mtmatch(device_t, cfdata_t, void *);
void mtattach(device_t, device_t, void *);
void mtdgram(device_t, struct mscp *, struct mscp_softc *);
void mtiodone(device_t, struct buf *);
int mtonline(device_t, struct mscp *);
int mtgotstatus(device_t, struct mscp *);
int mtioerror(device_t, struct mscp *, struct buf *);
void mtfillin(struct buf *, struct mscp *);
int mtcmd(struct mt_softc *, int, int, int);
void mtcmddone(device_t, struct mscp *);
int mt_putonline(struct mt_softc *);
struct mscp_device mt_device = {
mtdgram,
mtiodone,
mtonline,
NULL,
mtgotstatus,
0,
mtioerror,
0,
mtfillin,
mtcmddone,
};
#define mtunit(dev) (minor(dev) & T_UNIT)
#define mtnorewind(dev) (dev & T_NOREWIND)
#define mthdensity(dev) (dev & T_1600BPI)
CFATTACH_DECL_NEW(mt, sizeof(struct mt_softc),
mtmatch, mtattach, NULL, NULL);
extern struct cfdriver mt_cd;
dev_type_open(mtopen);
dev_type_close(mtclose);
dev_type_read(mtread);
dev_type_write(mtwrite);
dev_type_ioctl(mtioctl);
dev_type_strategy(mtstrategy);
dev_type_dump(mtdump);
const struct bdevsw mt_bdevsw = {
.d_open = mtopen,
.d_close = mtclose,
.d_strategy = mtstrategy,
.d_ioctl = mtioctl,
.d_dump = mtdump,
.d_psize = nosize,
.d_discard = nodiscard,
.d_flag = D_TAPE
};
const struct cdevsw mt_cdevsw = {
.d_open = mtopen,
.d_close = mtclose,
.d_read = mtread,
.d_write = mtwrite,
.d_ioctl = mtioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = D_TAPE
};
int
mtmatch(device_t parent, cfdata_t cf, void *aux)
{
struct drive_attach_args *da = aux;
struct mscp *mp = da->da_mp;
if ((da->da_typ & MSCPBUS_TAPE) == 0)
return 0;
if (cf->cf_loc[MSCPBUSCF_DRIVE] != MSCPBUSCF_DRIVE_DEFAULT &&
cf->cf_loc[MSCPBUSCF_DRIVE] != mp->mscp_unit)
return 0;
return 1;
}
void
mtattach(device_t parent, device_t self, void *aux)
{
struct mt_softc *mt = device_private(self);
struct drive_attach_args *da = aux;
struct mscp *mp = da->da_mp;
struct mscp_softc *mi = device_private(parent);
mt->mt_dev = self;
mt->mt_hwunit = mp->mscp_unit;
mi->mi_dp[mp->mscp_unit] = self;
disk_printtype(mp->mscp_unit, mp->mscp_guse.guse_mediaid);
}
int
mt_putonline(struct mt_softc *mt)
{
struct mscp *mp;
struct mscp_softc *mi =
device_private(device_parent(mt->mt_dev));
((volatile struct mt_softc *) mt)->mt_state = MT_OFFLINE;
mp = mscp_getcp(mi, MSCP_WAIT);
mp->mscp_opcode = M_OP_ONLINE;
mp->mscp_unit = mt->mt_hwunit;
mp->mscp_cmdref = (long)&mt->mt_state;
*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
if (tsleep(&mt->mt_state, PRIBIO, "mtonline", 240 * hz))
return MSCP_FAILED;
if ((volatile int)mt->mt_state != MT_ONLINE)
return MSCP_FAILED;
return MSCP_DONE;
}
int
mtopen(dev_t dev, int flag, int fmt, struct lwp *l)
{
struct mt_softc *mt;
int unit;
unit = mtunit(dev);
mt = device_lookup_private(&mt_cd, unit);
if (!mt)
return ENXIO;
if (mt->mt_inuse)
return EBUSY;
mt->mt_inuse = 1;
if (mt_putonline(mt) == MSCP_FAILED) {
mt->mt_inuse = 0;
return EIO;
}
return 0;
}
int
mtclose(dev_t dev, int flags, int fmt, struct lwp *l)
{
int unit = mtunit(dev);
struct mt_softc *mt = device_lookup_private(&mt_cd, unit);
if ((flags & FWRITE) && mt->mt_waswrite) {
mtcmd(mt, MTWEOF, 0, 0);
mtcmd(mt, MTWEOF, 0, 0);
mtcmd(mt, MTBSR, 1, 0);
}
if (mtnorewind(dev) == 0)
mtcmd(mt, MTREW, 0, 1);
if (mt->mt_serex)
mtcmd(mt, -1, 0, 0);
mt->mt_inuse = 0;
return 0;
}
void
mtstrategy(struct buf *bp)
{
int unit;
struct mt_softc *mt;
unit = mtunit(bp->b_dev);
if ((mt = device_lookup_private(&mt_cd, unit)) == NULL) {
bp->b_error = ENXIO;
biodone(bp);
return;
}
mt->mt_waswrite = bp->b_flags & B_READ ? 0 : 1;
mscp_strategy(bp, device_parent(mt->mt_dev));
return;
}
int
mtread(dev_t dev, struct uio *uio, int flag)
{
return (physio(mtstrategy, NULL, dev, B_READ, minphys, uio));
}
int
mtwrite(dev_t dev, struct uio *uio, int flag)
{
return (physio(mtstrategy, NULL, dev, B_WRITE, minphys, uio));
}
void
mtiodone(device_t usc, struct buf *bp)
{
biodone(bp);
}
void
mtfillin(struct buf *bp, struct mscp *mp)
{
int unit = mtunit(bp->b_dev);
struct mt_softc *mt = device_lookup_private(&mt_cd, unit);
mp->mscp_unit = mt->mt_hwunit;
if (mt->mt_serex == 2) {
mp->mscp_modifier = M_MD_CLSEX;
mt->mt_serex = 0;
} else
mp->mscp_modifier = 0;
mp->mscp_seq.seq_bytecount = bp->b_bcount;
}
void
mtdgram(device_t usc, struct mscp *mp, struct mscp_softc *mi)
{
if (mscp_decodeerror(usc == NULL?"unconf mt" : device_xname(usc), mp, mi))
return;
}
int
mtonline(device_t usc, struct mscp *mp)
{
struct mt_softc *mt = (void *)usc;
wakeup((void *)&mt->mt_state);
if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS)
mt->mt_state = MT_ONLINE;
return (MSCP_DONE);
}
int
mtgotstatus(device_t usc, struct mscp *mp)
{
return (MSCP_DONE);
}
static const char *mt_ioerrs[] = {
"invalid command",
"command aborted",
"unit offline",
"unknown",
"unknown",
"unit write protected",
"compare error",
"data error",
"host buffer access error",
"controller error",
"drive error",
"formatter error",
"BOT encountered",
"tape mark encountered",
"unknown",
"record data truncated",
};
int
mtioerror(device_t usc, struct mscp *mp, struct buf *bp)
{
struct mt_softc *mt = (void *)usc;
int st = mp->mscp_status & M_ST_MASK;
if (mp->mscp_flags & M_EF_SEREX)
mt->mt_serex = 1;
if (st == M_ST_TAPEMARK)
mt->mt_serex = 2;
else {
if (st && st < 17)
printf("%s: error %d (%s)\n", device_xname(mt->mt_dev), st,
mt_ioerrs[st-1]);
else
printf("%s: error %d\n", device_xname(mt->mt_dev), st);
bp->b_error = EROFS;
}
return (MSCP_DONE);
}
int
mtioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
int unit = mtunit(dev);
struct mt_softc *mt = device_lookup_private(&mt_cd, unit);
struct mtop *mtop;
int error = 0;
switch (cmd) {
case MTIOCTOP:
mtop = (void *)data;
if (mtop->mt_op == MTWEOF) {
while (mtop->mt_count-- > 0)
if ((error = mtcmd(mt, mtop->mt_op, 0, 0)))
break;
} else
error = mtcmd(mt, mtop->mt_op, mtop->mt_count, 0);
case MTIOCGET:
((struct mtget *)data)->mt_type = MT_ISTMSCP;
break;
default:
error = ENXIO;
break;
}
return (error);
}
int
mtdump(dev_t dev, daddr_t blkno, void *va, size_t size)
{
return -1;
}
int
mtcmd(struct mt_softc *mt, int cmd, int count, int complete)
{
struct mscp *mp;
struct mscp_softc *mi = device_private(device_parent(mt->mt_dev));
mp = mscp_getcp(mi, MSCP_WAIT);
mt->mt_ioctlerr = 0;
mp->mscp_unit = mt->mt_hwunit;
mp->mscp_cmdref = -1;
*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
switch (cmd) {
case MTWEOF:
mp->mscp_opcode = M_OP_WRITM;
break;
case MTBSF:
mp->mscp_modifier = M_MD_REVERSE;
case MTFSF:
mp->mscp_opcode = M_OP_POS;
mp->mscp_seq.seq_buffer = count;
break;
case MTBSR:
mp->mscp_modifier = M_MD_REVERSE;
case MTFSR:
mp->mscp_opcode = M_OP_POS;
mp->mscp_modifier |= M_MD_OBJCOUNT;
mp->mscp_seq.seq_bytecount = count;
break;
case MTREW:
mp->mscp_opcode = M_OP_POS;
mp->mscp_modifier = M_MD_REWIND | M_MD_CLSEX;
if (complete)
mp->mscp_modifier |= M_MD_IMMEDIATE;
mt->mt_serex = 0;
break;
case MTOFFL:
mp->mscp_opcode = M_OP_AVAILABLE;
mp->mscp_modifier = M_MD_UNLOAD | M_MD_CLSEX;
mt->mt_serex = 0;
break;
case MTNOP:
mp->mscp_opcode = M_OP_GETUNITST;
break;
case -1:
mp->mscp_opcode = M_OP_POS;
mp->mscp_modifier = M_MD_CLSEX;
mt->mt_serex = 0;
break;
default:
printf("Bad ioctl %x\n", cmd);
mp->mscp_opcode = M_OP_POS;
break;
}
bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
tsleep(&mt->mt_inuse, PRIBIO, "mtioctl", 0);
return mt->mt_ioctlerr;
}
void
mtcmddone(device_t usc, struct mscp *mp)
{
struct mt_softc *mt = (void *)usc;
if (mp->mscp_status) {
mt->mt_ioctlerr = EIO;
printf("%s: bad status %x\n", device_xname(mt->mt_dev),
mp->mscp_status);
}
wakeup(&mt->mt_inuse);
}