/*
 * Copyright (c) 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)ra.c	1.1 (2.10BSD Berkeley) 12/1/86
 */

/*
 * RAxx disk device driver
 * RQDX1 (rx50,rd50,rd51,rd52,rd53)
 */
#include <sys/param.h>

/* 
 * Moved to /usr/src/sys/stand
 * Peter Klapper, 16.10.2021
 */
#include "mscp.h"
#include "rareg.h"

#include <sys/inode.h>
#include "saio.h"

/*
 * RA Communications Area
*/
struct  rdca {
	short	ca_xxx1;	/* unused */
	char	ca_xxx2;	/* unused */
	char	ca_bdp;		/* BDP to purge */
	short	ca_cmdint;	/* command queue transition interrupt flag */
	short	ca_rspint;	/* response queue transition interrupt flag */
	short	ca_rspl;	/* response descriptors */
	short	ca_rsph;
	short	ca_cmdl;	/* command descriptors */
	short	ca_cmdh;
};

#define	ca_ringbase	ca_rspl

#define	RA_OWN	0x8000	/* RQ owns this descriptor */
#define	RA_INT	0x4000	/* allow interrupt on ring transition */

static	struct	radevice *RDADDR = ((struct radevice *)0172150);

static	struct	ra {
	struct	rdca	ra_ca;
	struct	mscp	ra_rsp;
	struct	mscp	ra_cmd;
} rd;

struct 	mscp 	*racmd();
static	int 	rdinit;
static	long	rdonline[8];

/*
 * XXX - Not consistent with other stand alone drivers.
 * static int	rd_boff[] = { 0, 9700, 27000, 30100, 0, 7460, 9700, 0 };
 */

raopen(io)
	register struct iob 	*io;
{
	register struct mscp 	*mp;
	int 			i;

	if( rdinit == 0 )
	{
		RDADDR->raip = 0;
		while((RDADDR->rasa & RA_STEP1) == 0);
		RDADDR->rasa = RA_ERR;
		while((RDADDR->rasa & RA_STEP2) == 0);
		RDADDR->rasa = (short)&rd.ra_ca.ca_ringbase;
		while((RDADDR->rasa & RA_STEP3) == 0);
		RDADDR->rasa = (short)(segflag & 3);
		while((RDADDR->rasa & RA_STEP4) == 0);
		RDADDR->rasa = RA_GO;
		rd.ra_ca.ca_rspl = (short)&rd.ra_rsp.m_cmdref;
		rd.ra_ca.ca_rsph = (short)(segflag & 3);
		rd.ra_ca.ca_cmdl = (short)&rd.ra_cmd.m_cmdref;
		rd.ra_ca.ca_cmdh = (short)(segflag & 3);
		rd.ra_cmd.m_cntflgs = 0;
		if( racmd(M_O_STCON) < 0 )
		{
			printf("RD: controller init error, STCON\n");
			return(-1);
		}
		rdinit = 1;
	}

	if( rdonline[io->i_unit & 7] == 0 )
		if( ramount(io) == -1 )
			return(-1);

	/*
	 * XXX - Not consistent with other stand alone drivers.
	 * if( (io->i_boff < 0) || (io->i_boff > 7) ||
	 *    (rd_boff[io->i_boff] == -1) )
	 * {
	 *	printf("RD: bad partition for unit=%d",io->i_unit & 7);
	 * }
	 * io->i_boff = rd_boff[io->i_boff];
	 */

	return(0);
}

raclose(io)
	register struct iob 	*io;
{
	rdonline[io->i_unit & 7] = 0;
	return(0);
}

ramount(io)
	register struct iob 	*io;
{
	if( racmd(M_O_ONLIN,io->i_unit) < 0 )
	{
		printf("RD: bring online error, unit=%d\n",io->i_unit & 7);
		return(-1);
	}
	rdonline[io->i_unit & 7] = rd.ra_rsp.m_uslow +  
	 	((long)(rd.ra_rsp.m_ushigh) << 16);
	return(0);
}

struct mscp *
racmd(op,unit)
	int op,unit;
{
	register	struct mscp 	*mp;
	register	int 		i;

	rd.ra_cmd.m_opcode = op;
	rd.ra_cmd.m_unit = unit & 7;
	rd.ra_rsp.m_header.ra_msglen = sizeof(struct mscp);
	rd.ra_cmd.m_header.ra_msglen = sizeof(struct mscp);
	rd.ra_ca.ca_rsph = RA_OWN | RA_INT | (segflag & 3);
	rd.ra_ca.ca_cmdh = RA_OWN | RA_INT | (segflag & 3);
	i = RDADDR->raip;
	while(1)
	{
		if( rd.ra_ca.ca_cmdint )
			rd.ra_ca.ca_cmdint = 0;
		if( rd.ra_ca.ca_rspint )
			break;
	}
	rd.ra_ca.ca_rspint = 0;
	mp = &rd.ra_rsp;
	if( ((mp->m_opcode & 0xff) != (op | M_O_END)) ||
	    ((mp->m_status & M_S_MASK) != M_S_SUCC) )
	{
		printf("RD: command error, unit=%d, opcode=%x, status=%x\n",
			unit & 7, mp->m_opcode & 0xff, mp->m_status);
		return((struct mscp *)-1);
	}
	return(0);
}

rastrategy(io, func)
	register struct iob *io;
{
	register struct mscp *mp;

	if( io->i_bn >= rdonline[io->i_unit & 7] )
		return(0);

	mp = &rd.ra_cmd;
	mp->m_lbn_l = loint(io->i_bn);
	mp->m_lbn_h = hiint(io->i_bn);
	mp->m_bytecnt = io->i_cc;
	mp->m_buf_l = (ushort)io->i_ma;
	mp->m_buf_h = segflag & 3;
	if( racmd(func == READ ? M_O_READ : M_O_WRITE,io->i_unit) < 0 )
		return(-1);

	return(io->i_cc);
}
