#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: crx.c,v 1.16 2023/12/17 15:06:33 andvar Exp $");
#include <sys/param.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <machine/ka820.h>
#include <vax/vax/crx.h>
static dev_type_open(crxopen);
static dev_type_close(crxclose);
static dev_type_read(crxrw);
const struct cdevsw crx_cdevsw = {
.d_open = crxopen,
.d_close = crxclose,
.d_read = crxrw,
.d_write = crxrw,
.d_ioctl = noioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = 0
};
extern struct rx50device *rx50device_ptr;
#define rxaddr rx50device_ptr
extern struct ka820port *ka820port_ptr;
#define rx50unit(dev) minor(dev)
struct rx50state {
short rs_flags;
short rs_drive;
u_int rs_blkno;
} rx50state;
#define RS_0OPEN 0x01
#define RS_1OPEN 0x02
#define RS_BUSY 0x04
#define RS_WANT 0x08
#define RS_DONE 0x20
#define RS_ERROR 0x40
#if 0
#define CRXDEBUG 1
#endif
int
crxopen(dev_t dev, int flags, int fmt, struct lwp *l)
{
int unit;
#if CRXDEBUG
printf("crxopen(csa%d)\n", rx50unit(dev));
#endif
if ((unit = rx50unit(dev)) >= 2)
return (ENXIO);
if (rx50state.rs_flags & (1 << unit))
return (EBUSY);
rx50state.rs_flags |= 1 << unit;
return (0);
}
int
crxclose(dev_t dev, int flags, int fmt, struct lwp *l)
{
#if CRXDEBUG
printf("crxclose(csa%d)\n", rx50unit(dev));
#endif
rx50state.rs_flags &= ~(1 << rx50unit(dev));
return 0;
}
int
crxrw(dev_t dev, struct uio *uio, int flags)
{
struct rx50state *rs;
char *cp;
int error, i, t;
char secbuf[512];
static char driveselect[2] = { RXCMD_DRIVE0, RXCMD_DRIVE1 };
#if CRXDEBUG
printf("crxrw(csa%d): %s\n",
rx50unit(dev), uio->uio_rw==UIO_READ?"read":"write");
printf("crxrw: ka820port = %lx\n", ka820port_ptr->csr);
#endif
if ((uio->uio_offset & 511) || (uio->uio_resid & 511))
return (EINVAL);
rs = &rx50state;
i = splvm();
while (rs->rs_flags & RS_BUSY) {
rs->rs_flags |= RS_WANT;
(void) tsleep(&rx50state, PRIBIO, "crxbusy", 0);
}
rs->rs_flags |= RS_BUSY;
rs->rs_drive = rx50unit(dev);
splx(i);
rxaddr = rx50device_ptr;
error = 0;
while (uio->uio_resid) {
rs->rs_blkno = uio->uio_offset >> 9;
if (rs->rs_blkno >= RX50MAXSEC) {
if (rs->rs_blkno > RX50MAXSEC)
error = EINVAL;
else if (uio->uio_rw == UIO_WRITE)
error = ENOSPC;
break;
}
rs->rs_flags &= ~(RS_ERROR | RS_DONE);
if (uio->uio_rw == UIO_WRITE) {
error = uiomove(secbuf, 512, uio);
if (error)
break;
i = rxaddr->rxrda;
for (cp = secbuf, i = 512; --i >= 0;)
rxaddr->rxfdb = *cp++;
i = RXCMD_WRITE;
} else
i = RXCMD_READ;
rxaddr->rxcmd = i | driveselect[rs->rs_drive];
i = rs->rs_blkno - ((t = rs->rs_blkno / RX50SEC) * RX50SEC);
rxaddr->rxtrk = t == 79 ? 0 : t + 1;
#ifdef notdef
rxaddr->rxsec = "\1\3\5\7\11\1\3\5\7"[(2*t + i) % 5] + (i > 4);
#else
rxaddr->rxsec = RX50SKEW(i, t);
#endif
#if CRXDEBUG
printf("crx: going off\n");
printf("crxrw: ka820port = %lx\n", ka820port_ptr->csr);
#endif
rxaddr->rxgo = 0;
ka820port_ptr->csr |= KA820PORT_RXIRQ;
i = splvm();
while ((rs->rs_flags & RS_DONE) == 0) {
#if CRXDEBUG
printf("crx: sleeping on I/O\n");
printf("crxopen: ka820port = %lx\n", ka820port_ptr->csr);
#endif
(void) tsleep(&rs->rs_blkno, PRIBIO, "crxrw", 0);
}
splx(i);
if (rs->rs_flags & RS_ERROR) {
error = EIO;
break;
}
if (uio->uio_rw == UIO_READ) {
i = rxaddr->rxrda;
for (cp = secbuf, i = 512; --i >= 0;)
*cp++ = rxaddr->rxedb;
error = uiomove(secbuf, 512, uio);
if (error)
break;
}
}
#if CRXDEBUG
printf("crx: let others in\n");
#endif
rs->rs_flags &= ~RS_BUSY;
if (rs->rs_flags & RS_WANT)
wakeup((void *) rs);
return (error);
}
void
crxintr(void *arg)
{
struct rx50state *rs = &rx50state;
if ((rxaddr->rxcmd & RXCMD_DONE) == 0)
return;
if ((rs->rs_flags & RS_BUSY) == 0) {
printf("stray rx50 interrupt ignored (rs_flags: 0x%x, rxcmd: 0x%x)\n",
rs->rs_flags, rxaddr->rxcmd);
return;
}
if (rxaddr->rxcmd & RXCMD_ERROR) {
printf(
"csa%d: hard error sn%d: cmd=%x trk=%x sec=%x csc=%x ict=%x ext=%x\n",
rs->rs_drive + 1, rs->rs_blkno,
rxaddr->rxcmd, rxaddr->rxtrk, rxaddr->rxsec,
rxaddr->rxcsc, rxaddr->rxict, rxaddr->rxext);
rxaddr->rxcmd = RXCMD_RESET;
rxaddr->rxgo = 0;
rs->rs_flags |= RS_ERROR;
}
rs->rs_flags |= RS_DONE;
wakeup((void *) &rs->rs_blkno);
}