#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ppbus_msq.c,v 1.12 2022/05/31 08:43:16 andvar Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <dev/ppbus/ppbus_conf.h>
#include <dev/ppbus/ppbus_base.h>
#include <dev/ppbus/ppbus_device.h>
#include <dev/ppbus/ppbus_msq.h>
#include <dev/ppbus/ppbus_var.h>
#define COMPAT_MSQ 0x0
#define NIBBLE_MSQ 0x1
#define PS2_MSQ 0x2
#define EPP17_MSQ 0x3
#define EPP19_MSQ 0x4
#define ECP_MSQ 0x5
static struct ppbus_xfer * mode2xfer(struct ppbus_softc *,
struct ppbus_device_softc *, int);
static struct ppbus_xfer *
mode2xfer(struct ppbus_softc * bus, struct ppbus_device_softc * ppbdev,
int opcode)
{
int index;
unsigned int epp;
struct ppbus_xfer * table;
switch (opcode) {
case MS_OP_GET:
table = ppbdev->get_xfer;
break;
case MS_OP_PUT:
table = ppbdev->put_xfer;
break;
default:
panic("%s: unknown opcode (%d)", __func__, opcode);
}
switch (bus->sc_mode) {
case PPBUS_COMPATIBLE:
index = COMPAT_MSQ;
break;
case PPBUS_NIBBLE:
index = NIBBLE_MSQ;
break;
case PPBUS_PS2:
index = PS2_MSQ;
break;
case PPBUS_EPP:
ppbus_read_ivar(bus->sc_dev, PPBUS_IVAR_EPP_PROTO, &epp);
switch (epp) {
case PPBUS_EPP_1_7:
index = EPP17_MSQ;
break;
case PPBUS_EPP_1_9:
index = EPP19_MSQ;
break;
default:
panic("%s: unknown EPP protocol [%u]!", __func__, epp);
}
break;
case PPBUS_ECP:
index = ECP_MSQ;
break;
default:
panic("%s: unknown mode (%d)", __func__, ppbdev->mode);
}
return (&table[index]);
}
int
ppbus_MS_init(device_t dev, device_t ppbdev,
struct ppbus_microseq * loop, int opcode)
{
struct ppbus_softc * bus = device_private(dev);
struct ppbus_xfer *xfer = mode2xfer(bus, (struct ppbus_device_softc *)
ppbdev, opcode);
xfer->loop = loop;
return 0;
}
int
ppbus_MS_exec(device_t ppb, device_t dev,
int opcode, union ppbus_insarg param1, union ppbus_insarg param2,
union ppbus_insarg param3, int * ret)
{
struct ppbus_microseq msq[] = {
{ MS_UNKNOWN, { { MS_UNKNOWN }, { MS_UNKNOWN },
{ MS_UNKNOWN } } },
MS_RET(0)
};
msq[0].opcode = opcode;
msq[0].arg[0] = param1;
msq[0].arg[1] = param2;
msq[0].arg[2] = param3;
return (ppbus_MS_microseq(ppb, dev, msq, ret));
}
int
ppbus_MS_loop(device_t ppb, device_t dev,
struct ppbus_microseq * prolog, struct ppbus_microseq * body,
struct ppbus_microseq * epilog, int iter, int * ret)
{
struct ppbus_microseq loop_microseq[] = {
MS_CALL(0),
MS_SET(MS_UNKNOWN),
MS_CALL(0),
MS_DBRA(-1 ),
MS_CALL(0),
MS_RET(0)
};
loop_microseq[0].arg[0].p = (void *)prolog;
loop_microseq[1].arg[0].i = iter;
loop_microseq[2].arg[0].p = (void *)body;
loop_microseq[4].arg[0].p = (void *)epilog;
return (ppbus_MS_microseq(ppb, dev, loop_microseq, ret));
}
int
ppbus_MS_init_msq(struct ppbus_microseq * msq, int nbparam, ...)
{
int i;
int param, ins, arg, type;
va_list p_list;
va_start(p_list, nbparam);
for(i = 0; i < nbparam; i++) {
param = va_arg(p_list, int);
ins = MS_INS(param);
arg = MS_ARG(param);
type = MS_TYP(param);
if (arg >= PPBUS_MS_MAXARGS)
panic("%s: parameter out of range (0x%x)!", __func__,
param);
#if 0
printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
__func__, param, ins, arg, type);
#endif
switch (type) {
case MS_TYP_INT:
msq[ins].arg[arg].i = va_arg(p_list, int);
break;
case MS_TYP_CHA:
msq[ins].arg[arg].i = (int)va_arg(p_list, int);
break;
case MS_TYP_PTR:
msq[ins].arg[arg].p = va_arg(p_list, void *);
break;
case MS_TYP_FUN:
msq[ins].arg[arg].f = va_arg(p_list, void *);
break;
default:
panic("%s: unknown parameter (0x%x)!", __func__, param);
}
}
va_end(p_list);
return (0);
}
int
ppbus_MS_microseq(device_t dev, device_t busdev,
struct ppbus_microseq * msq, int * ret)
{
struct ppbus_device_softc * ppbdev = device_private(busdev);
struct ppbus_softc * bus = device_private(dev);
struct ppbus_microseq * mi;
size_t cnt;
int error;
struct ppbus_xfer * xfer;
struct ppbus_microseq initxfer[] = {
MS_PTR(MS_UNKNOWN),
MS_SET(MS_UNKNOWN),
MS_RET(0)
};
if(bus->ppbus_owner != busdev) {
return (EACCES);
}
#define INCR_PC (mi ++)
mi = msq;
again:
for (;;) {
switch (mi->opcode) {
case MS_OP_PUT:
case MS_OP_GET:
xfer = mode2xfer(bus, ppbdev, mi->opcode);
if (!xfer->loop) {
if (mi->opcode == MS_OP_PUT) {
if ((error = ppbus_write(
bus->sc_dev,
(char *)mi->arg[0].p,
mi->arg[1].i, 0, &cnt))) {
goto error;
}
INCR_PC;
goto again;
}
else {
panic("%s: IEEE1284 read not supported",
__func__);
}
}
initxfer[0].arg[0].p = mi->arg[0].p;
initxfer[1].arg[0].i = mi->arg[1].i;
ppbus_MS_microseq(dev, busdev, initxfer, &error);
if (error)
goto error;
ppbus_MS_microseq(dev, busdev, xfer->loop, &error);
if (error)
goto error;
INCR_PC;
break;
case MS_OP_RET:
if (ret)
*ret = mi->arg[0].i;
return (0);
break;
default:
if((error =
bus->ppbus_exec_microseq(
bus->sc_dev, &mi))) {
goto error;
}
break;
}
}
error:
return (error);
}