diff options
Diffstat (limited to 'sys/dev/ieee488')
| -rw-r--r-- | sys/dev/ieee488/ibfoo.c | 1005 | ||||
| -rw-r--r-- | sys/dev/ieee488/ibfoo_int.h | 147 | ||||
| -rw-r--r-- | sys/dev/ieee488/pcii.c | 192 | ||||
| -rw-r--r-- | sys/dev/ieee488/ugpib.h | 155 | ||||
| -rw-r--r-- | sys/dev/ieee488/upd7210.c | 291 | ||||
| -rw-r--r-- | sys/dev/ieee488/upd7210.h | 234 |
6 files changed, 2024 insertions, 0 deletions
diff --git a/sys/dev/ieee488/ibfoo.c b/sys/dev/ieee488/ibfoo.c new file mode 100644 index 000000000000..537443dbf091 --- /dev/null +++ b/sys/dev/ieee488/ibfoo.c @@ -0,0 +1,1005 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * High-level driver for µPD7210 based GPIB cards. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +# define IBDEBUG +# undef IBDEBUG + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <isa/isavar.h> + +#include <dev/ieee488/ugpib.h> + +#define UPD7210_SW_DRIVER +#include <dev/ieee488/upd7210.h> + +static MALLOC_DEFINE(M_IBFOO, "IBFOO", "IBFOO"); + + +/* ibfoo API */ + +#include <dev/ieee488/ibfoo_int.h> + +/* XXX: This is really a bitmap */ +enum h_kind { + H_DEV = 1, + H_BOARD = 2, + H_EITHER = 3 +}; + +struct handle { + LIST_ENTRY(handle) list; + int handle; + enum h_kind kind; + int pad; + int sad; + struct timeval timeout; + int eot; + int eos; + int dma; +}; + +struct ibfoo { + struct upd7210 *u; + LIST_HEAD(,handle) handles; + struct unrhdr *unrhdr; + struct callout callout; + struct handle *h; + struct ibarg *ap; + + enum { + IDLE, + BUSY, + PIO_IDATA, + PIO_ODATA, + PIO_CMD, + DMA_IDATA + } mode; + + struct timeval deadline; + + struct handle *rdh; /* addressed for read */ + struct handle *wrh; /* addressed for write */ + + int doeoi; + + u_char *buf; + u_int buflen; +}; + +typedef int ibhandler_t(struct ibfoo *ib); + +static struct timeval timeouts[] = { + [TNONE] = { 0, 0}, + [T10us] = { 0, 10}, + [T30us] = { 0, 30}, + [T100us] = { 0, 100}, + [T300us] = { 0, 300}, + [T1ms] = { 0, 1000}, + [T3ms] = { 0, 3000}, + [T10ms] = { 0, 10000}, + [T30ms] = { 0, 30000}, + [T100ms] = { 0, 100000}, + [T300ms] = { 0, 300000}, + [T1s] = { 1, 0}, + [T3s] = { 3, 0}, + [T10s] = { 10, 0}, + [T30s] = { 30, 0}, + [T100s] = { 100, 0}, + [T300s] = { 300, 0}, + [T1000s] = { 1000, 0} +}; + +static const u_int max_timeouts = sizeof timeouts / sizeof timeouts[0]; + +static int ibdebug; + +static int +ib_set_error(struct ibarg *ap, int error) +{ + + if (ap->__iberr == 0) + ap->__iberr = error; + ap->__ibsta |= ERR; + ap->__retval = ap->__ibsta; + return (0); +} + +static int +ib_had_timeout(struct ibarg *ap) +{ + + ib_set_error(ap, EABO); + ap->__ibsta |= TIMO; + ap->__retval = ap->__ibsta; + return (0); +} + +static int +ib_set_errno(struct ibarg *ap, int errno) +{ + + if (ap->__iberr == 0) { + ap->__iberr = EDVR; + ap->__ibcnt = errno; + } + ap->__ibsta |= ERR; + ap->__retval = ap->__ibsta; + return (0); +} + +static int +gpib_ib_irq(struct upd7210 *u, int intr __unused) +{ + struct ibfoo *ib; + + ib = u->ibfoo; + + mtx_assert(&u->mutex, MA_OWNED); + switch (ib->mode) { + case PIO_CMD: + if (!(u->rreg[ISR2] & IXR2_CO)) + return (0); + if (ib->buflen == 0) + break; + upd7210_wr(u, CDOR, *ib->buf); + ib->buf++; + ib->buflen--; + return (1); + case PIO_IDATA: + if (!(u->rreg[ISR1] & IXR1_DI)) + return (0); + *ib->buf = upd7210_rd(u, DIR); + ib->buf++; + ib->buflen--; + if (ib->buflen == 0 || (u->rreg[ISR1] & IXR1_ENDRX)) + break; + return (1); + case PIO_ODATA: + if (!(u->rreg[ISR1] & IXR1_DO)) + return (0); + if (ib->buflen == 0) + break; + if (ib->buflen == 1 && ib->doeoi) + upd7210_wr(u, AUXMR, AUXMR_SEOI); + upd7210_wr(u, CDOR, *ib->buf); + ib->buf++; + ib->buflen--; + return (1); + case DMA_IDATA: + if (!(u->rreg[ISR1] & IXR1_ENDRX)) + return (0); + break; + default: + return (0); + } + upd7210_wr(u, IMR1, 0); + upd7210_wr(u, IMR2, 0); + ib->mode = BUSY; + wakeup(&ib->buflen); + return (1); +} + +static void +gpib_ib_timeout(void *arg) +{ + struct upd7210 *u; + struct ibfoo *ib; + struct timeval tv; + + u = arg; + ib = u->ibfoo; + mtx_lock(&u->mutex); + if (ib->mode == DMA_IDATA && isa_dmatc(u->dmachan)) { + KASSERT(u->dmachan >= 0, ("Bogus dmachan = %d", u->dmachan)); + upd7210_wr(u, IMR1, 0); + upd7210_wr(u, IMR2, 0); + ib->mode = BUSY; + wakeup(&ib->buflen); + } + if (ib->mode > BUSY) { + upd7210_rd(u, ISR1); + upd7210_rd(u, ISR2); + gpib_ib_irq(u, 2); + } + if (ib->mode != IDLE && timevalisset(&ib->deadline)) { + getmicrouptime(&tv); + if (timevalcmp(&ib->deadline, &tv, <)) { + ib_had_timeout(ib->ap); + upd7210_wr(u, IMR1, 0); + upd7210_wr(u, IMR2, 0); + ib->mode = BUSY; + wakeup(&ib->buflen); + } + } + if (ib->mode != IDLE) + callout_reset(&ib->callout, hz / 5, gpib_ib_timeout, arg); + mtx_unlock(&u->mutex); +} + +static void +gpib_ib_wait_xfer(struct upd7210 *u, struct ibfoo *ib) +{ + int i; + + mtx_assert(&u->mutex, MA_OWNED); + while (ib->mode > BUSY) { + i = msleep(&ib->buflen, &u->mutex, + PZERO | PCATCH, "ibwxfr", 0); + if (i == EINTR) { + ib_set_errno(ib->ap, i); + break; + } + if (u->rreg[ISR1] & IXR1_ERR) { + ib_set_error(ib->ap, EABO); /* XXX ? */ + break; + } + } + ib->mode = BUSY; + ib->buf = NULL; + upd7210_wr(u, IMR1, 0); + upd7210_wr(u, IMR2, 0); +} + +static void +config_eos(struct upd7210 *u, struct handle *h) +{ + int i; + + i = 0; + if (h->eos & REOS) { + upd7210_wr(u, EOSR, h->eos & 0xff); + i |= AUXA_REOS; + } + if (h->eos & XEOS) { + upd7210_wr(u, EOSR, h->eos & 0xff); + i |= AUXA_XEOS; + } + if (h->eos & BIN) + i |= AUXA_BIN; + upd7210_wr(u, AUXRA, C_AUXA | i); +} + +/* + * Look up the handle, and set the deadline if the handle has a timeout. + */ +static int +gethandle(struct upd7210 *u, struct ibarg *ap, struct handle **hp) +{ + struct ibfoo *ib; + struct handle *h; + + KASSERT(ap->__field & __F_HANDLE, ("gethandle without __F_HANDLE")); + ib = u->ibfoo; + LIST_FOREACH(h, &ib->handles, list) { + if (h->handle == ap->handle) { + *hp = h; + return (0); + } + } + ib_set_error(ap, EARG); + return (1); +} + +static int +pio_cmd(struct upd7210 *u, u_char *cmd, int len) +{ + struct ibfoo *ib; + + ib = u->ibfoo; + + if (ib->rdh != NULL || ib->wrh != NULL) { + upd7210_take_ctrl_async(u); + ib->rdh = NULL; + ib->wrh = NULL; + } + mtx_lock(&u->mutex); + ib->mode = PIO_CMD; + ib->buf = cmd; + ib->buflen = len; + upd7210_wr(u, IMR2, IXR2_CO); + + gpib_ib_irq(u, 1); + + gpib_ib_wait_xfer(u, ib); + + mtx_unlock(&u->mutex); + return (len - ib->buflen); +} + +static int +pio_odata(struct upd7210 *u, u_char *data, int len) +{ + struct ibfoo *ib; + + ib = u->ibfoo; + + if (len == 0) + return (0); + mtx_lock(&u->mutex); + ib->mode = PIO_ODATA; + ib->buf = data; + ib->buflen = len; + upd7210_wr(u, IMR1, IXR1_DO); + + gpib_ib_wait_xfer(u, ib); + + mtx_unlock(&u->mutex); + return (len - ib->buflen); +} + +static int +pio_idata(struct upd7210 *u, u_char *data, int len) +{ + struct ibfoo *ib; + + ib = u->ibfoo; + + mtx_lock(&u->mutex); + ib->mode = PIO_IDATA; + ib->buf = data; + ib->buflen = len; + upd7210_wr(u, IMR1, IXR1_DI); + + gpib_ib_wait_xfer(u, ib); + + mtx_unlock(&u->mutex); + return (len - ib->buflen); +} + +static int +dma_idata(struct upd7210 *u, u_char *data, int len) +{ + int j; + struct ibfoo *ib; + + KASSERT(u->dmachan >= 0, ("Bogus dmachan %d", u->dmachan)); + ib = u->ibfoo; + ib->mode = DMA_IDATA; + mtx_lock(&Giant); + isa_dmastart(ISADMA_READ, data, len, u->dmachan); + mtx_unlock(&Giant); + mtx_lock(&u->mutex); + upd7210_wr(u, IMR1, IXR1_ENDRX); + upd7210_wr(u, IMR2, IMR2_DMAI); + gpib_ib_wait_xfer(u, ib); + mtx_unlock(&u->mutex); + mtx_lock(&Giant); + j = isa_dmastatus(u->dmachan); + isa_dmadone(ISADMA_READ, data, len, u->dmachan); + mtx_unlock(&Giant); + return (len - j); +} + +static int +ib_send_msg(struct ibfoo *ib, int msg) +{ + u_char buf[10]; + int i, j; + + i = 0; + buf[i++] = UNT; + buf[i++] = UNL; + buf[i++] = LAD | ib->h->pad; + if (ib->h->sad) + buf[i++] = LAD | TAD | ib->h->sad; + buf[i++] = TAD | 0; + buf[i++] = msg; + j = pio_cmd(ib->u, buf, i); + if (i != j) + ib_set_error(ib->ap, EABO); /* XXX ? */ + return (0); +} + +static int +ibask(struct ibfoo *ib) +{ /* XXX */ + + ibdebug = ib->ap->option; + return (0); +} + +#define ibbna NULL +#define ibcac NULL + +static int +ibclr(struct ibfoo *ib) +{ + + return (ib_send_msg(ib, SDC)); +} + +#define ibcmd NULL +#define ibcmda NULL +#define ibconfig NULL + +static int +ibdev(struct ibfoo *ib) +{ /* TBD */ + struct handle *h; + + h = malloc(sizeof *h, M_IBFOO, M_ZERO | M_WAITOK); + h->handle = alloc_unr(ib->unrhdr); + h->kind = H_DEV; + h->pad = ib->ap->pad; + h->sad = ib->ap->sad; + h->timeout = timeouts[ib->ap->tmo]; + h->eot = ib->ap->eot; + h->eos = ib->ap->eos; + mtx_lock(&ib->u->mutex); + LIST_INSERT_HEAD(&ib->handles, h, list); + mtx_unlock(&ib->u->mutex); + ib->ap->__retval = h->handle; + return (0); +} + +#define ibdiag NULL + +static int +ibdma(struct ibfoo *ib) +{ + + if (ib->u->dmachan < 0 && ib->ap->v) + return (ib_set_error(ib->ap, EARG)); + ib->h->dma = ib->ap->v; + return (0); +} + +static int +ibeos(struct ibfoo *ib) +{ + + ib->ap->__iberr = ib->h->eos; + ib->h->eos = ib->ap->eos; + if (ib->rdh == ib->h) + config_eos(ib->u, ib->h); + return (0); +} + +static int +ibeot(struct ibfoo *ib) +{ + + ib->h->eot = ib->ap->eot; + return (0); +} + +#define ibevent NULL +#define ibfind NULL +#define ibgts NULL +#define ibist NULL +#define iblines NULL +#define ibllo NULL +#define ibln NULL + +static int +ibloc(struct ibfoo *ib) +{ /* XXX */ + + if (ib->h->kind == H_BOARD) + return (EOPNOTSUPP); /* XXX */ + return (ib_send_msg(ib, GTL)); +} + +static int +ibonl(struct ibfoo *ib) +{ /* XXX */ + + if (ib->ap->v) + return (EOPNOTSUPP); /* XXX */ + mtx_lock(&ib->u->mutex); + LIST_REMOVE(ib->h, list); + mtx_unlock(&ib->u->mutex); + free(ib->h, M_IBFOO); + ib->h = NULL; + return (0); +} + +static int +ibpad(struct ibfoo *ib) +{ + + ib->h->pad = ib->ap->pad; + return (0); +} + +#define ibpct NULL +#define ibpoke NULL +#define ibppc NULL + +static int +ibrd(struct ibfoo *ib) +{ /* TBD */ + u_char buf[10], *bp; + int i, j, error, bl, bc; + u_char *dp; + + if (ib->h->kind == H_BOARD) + return (EOPNOTSUPP); /* XXX */ + bl = ib->ap->cnt; + if (bl > PAGE_SIZE) + bl = PAGE_SIZE; + bp = malloc(bl, M_IBFOO, M_WAITOK); + + if (ib->rdh != ib->h) { + i = 0; + buf[i++] = UNT; + buf[i++] = UNL; + buf[i++] = LAD | 0; + buf[i++] = TAD | ib->h->pad; + if (ib->h->sad) + buf[i++] = ib->h->sad; + i = pio_cmd(ib->u, buf, i); + config_eos(ib->u, ib->h); + ib->rdh = ib->h; + ib->wrh = NULL; + } + upd7210_goto_standby(ib->u); + dp = ib->ap->buffer; + bc = ib->ap->cnt; + error = 0; + while (bc > 0 && ib->ap->__iberr == 0) { + j = imin(bc, PAGE_SIZE); + if (ib->h->dma) + i = dma_idata(ib->u, bp, j); + else + i = pio_idata(ib->u, bp, j); + error = copyout(bp, dp , i); + if (error) + break; + ib->ap->__ibcnt += i; + if (i != j) + break; + bc -= i; + dp += i; + } + upd7210_take_ctrl_async(ib->u); + free(bp, M_IBFOO); + return (error); +} + +#define ibrda NULL +#define ibrdf NULL +#define ibrdkey NULL +#define ibrpp NULL +#define ibrsc NULL +#define ibrsp NULL +#define ibrsv NULL + +static int +ibsad(struct ibfoo *ib) +{ + + ib->h->sad = ib->ap->sad; + return (0); +} + +#define ibsgnl NULL + +static int +ibsic(struct ibfoo *ib) +{ /* TBD */ + + upd7210_wr(ib->u, AUXMR, AUXMR_SIFC); + DELAY(100); + upd7210_wr(ib->u, AUXMR, AUXMR_CIFC); + return (0); +} + +#define ibsre NULL +#define ibsrq NULL +#define ibstop NULL + +static int +ibtmo(struct ibfoo *ib) +{ + + ib->h->timeout = timeouts[ib->ap->tmo]; + return (0); +} + +#define ibtrap NULL + +static int +ibtrg(struct ibfoo *ib) +{ + + return (ib_send_msg(ib, GET)); +} + +#define ibwait NULL + +static int +ibwrt(struct ibfoo *ib) +{ /* XXX */ + u_char buf[10], *bp; + int i; + + if (ib->h->kind == H_BOARD) + return (EOPNOTSUPP); + bp = malloc(ib->ap->cnt, M_IBFOO, M_WAITOK); + /* XXX: bigger than PAGE_SIZE handling */ + i = copyin(ib->ap->buffer, bp, ib->ap->cnt); + if (i) { + free(bp, M_IBFOO); + return (i); + } + if (ib->wrh != ib->h) { + i = 0; + buf[i++] = UNT; + buf[i++] = UNL; + buf[i++] = LAD | ib->h->pad; + if (ib->h->sad) + buf[i++] = LAD | TAD | ib->h->sad; + buf[i++] = TAD | 0; + i = pio_cmd(ib->u, buf, i); + ib->rdh = NULL; + ib->wrh = ib->h; + config_eos(ib->u, ib->h); + } + upd7210_goto_standby(ib->u); + ib->doeoi = ib->h->eot; + i = pio_odata(ib->u, bp, ib->ap->cnt); + upd7210_take_ctrl_async(ib->u); + ib->ap->__ibcnt = i; + free(bp, M_IBFOO); + return (0); +} + +#define ibwrta NULL +#define ibwrtf NULL +#define ibwrtkey NULL +#define ibxtrc NULL + +static struct ibhandler { + const char *name; + enum h_kind kind; + ibhandler_t *func; + u_int args; +} ibhandlers[] = { + [__ID_IBASK] = { "ibask", H_EITHER, ibask, __F_HANDLE | __F_OPTION | __F_RETVAL }, + [__ID_IBBNA] = { "ibbna", H_DEV, ibbna, __F_HANDLE | __F_BDNAME }, + [__ID_IBCAC] = { "ibcac", H_BOARD, ibcac, __F_HANDLE | __F_V }, + [__ID_IBCLR] = { "ibclr", H_DEV, ibclr, __F_HANDLE }, + [__ID_IBCMD] = { "ibcmd", H_BOARD, ibcmd, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBCMDA] = { "ibcmda", H_BOARD, ibcmda, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBCONFIG] = { "ibconfig", H_EITHER, ibconfig, __F_HANDLE | __F_OPTION | __F_VALUE }, + [__ID_IBDEV] = { "ibdev", 0, ibdev, __F_BOARDID | __F_PAD | __F_SAD | __F_TMO | __F_EOT | __F_EOS }, + [__ID_IBDIAG] = { "ibdiag", H_EITHER, ibdiag, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBDMA] = { "ibdma", H_EITHER, ibdma, __F_HANDLE | __F_V }, + [__ID_IBEOS] = { "ibeos", H_EITHER, ibeos, __F_HANDLE | __F_EOS }, + [__ID_IBEOT] = { "ibeot", H_EITHER, ibeot, __F_HANDLE | __F_EOT }, + [__ID_IBEVENT] = { "ibevent", H_BOARD, ibevent, __F_HANDLE | __F_EVENT }, + [__ID_IBFIND] = { "ibfind", 0, ibfind, __F_BDNAME }, + [__ID_IBGTS] = { "ibgts", H_BOARD, ibgts, __F_HANDLE | __F_V }, + [__ID_IBIST] = { "ibist", H_BOARD, ibist, __F_HANDLE | __F_V }, + [__ID_IBLINES] = { "iblines", H_BOARD, iblines, __F_HANDLE | __F_LINES }, + [__ID_IBLLO] = { "ibllo", H_EITHER, ibllo, __F_HANDLE }, + [__ID_IBLN] = { "ibln", H_BOARD, ibln, __F_HANDLE | __F_PADVAL | __F_SADVAL | __F_LISTENFLAG }, + [__ID_IBLOC] = { "ibloc", H_EITHER, ibloc, __F_HANDLE }, + [__ID_IBONL] = { "ibonl", H_EITHER, ibonl, __F_HANDLE | __F_V }, + [__ID_IBPAD] = { "ibpad", H_EITHER, ibpad, __F_HANDLE | __F_PAD }, + [__ID_IBPCT] = { "ibpct", H_DEV, ibpct, __F_HANDLE }, + [__ID_IBPOKE] = { "ibpoke", H_EITHER, ibpoke, __F_HANDLE | __F_OPTION | __F_VALUE }, + [__ID_IBPPC] = { "ibppc", H_EITHER, ibppc, __F_HANDLE | __F_V }, + [__ID_IBRD] = { "ibrd", H_EITHER, ibrd, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBRDA] = { "ibrda", H_EITHER, ibrda, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBRDF] = { "ibrdf", H_EITHER, ibrdf, __F_HANDLE | __F_FLNAME }, + [__ID_IBRDKEY] = { "ibrdkey", H_EITHER, ibrdkey, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBRPP] = { "ibrpp", H_EITHER, ibrpp, __F_HANDLE | __F_PPR }, + [__ID_IBRSC] = { "ibrsc", H_BOARD, ibrsc, __F_HANDLE | __F_V }, + [__ID_IBRSP] = { "ibrsp", H_DEV, ibrsp, __F_HANDLE | __F_SPR }, + [__ID_IBRSV] = { "ibrsv", H_EITHER, ibrsv, __F_HANDLE | __F_V }, + [__ID_IBSAD] = { "ibsad", H_EITHER, ibsad, __F_HANDLE | __F_SAD }, + [__ID_IBSGNL] = { "ibsgnl", H_EITHER, ibsgnl, __F_HANDLE | __F_V }, + [__ID_IBSIC] = { "ibsic", H_BOARD, ibsic, __F_HANDLE }, + [__ID_IBSRE] = { "ibsre", H_BOARD, ibsre, __F_HANDLE | __F_V }, + [__ID_IBSRQ] = { "ibsrq", H_EITHER, ibsrq, __F_FUNC }, + [__ID_IBSTOP] = { "ibstop", H_EITHER, ibstop, __F_HANDLE }, + [__ID_IBTMO] = { "ibtmo", H_EITHER, ibtmo, __F_HANDLE | __F_TMO }, + [__ID_IBTRAP] = { "ibtrap", H_EITHER, ibtrap, __F_MASK | __F_MODE }, + [__ID_IBTRG] = { "ibtrg", H_DEV, ibtrg, __F_HANDLE }, + [__ID_IBWAIT] = { "ibwait", H_EITHER, ibwait, __F_HANDLE | __F_MASK }, + [__ID_IBWRT] = { "ibwrt", H_EITHER, ibwrt, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBWRTA] = { "ibwrta", H_EITHER, ibwrta, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBWRTF] = { "ibwrtf", H_EITHER, ibwrtf, __F_HANDLE | __F_FLNAME }, + [__ID_IBWRTKEY] = { "ibwrtkey", H_EITHER, ibwrtkey, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBXTRC] = { "ibxtrc", H_EITHER, ibxtrc, __F_HANDLE | __F_BUFFER | __F_CNT }, +}; + +static const u_int max_ibhandler = sizeof ibhandlers / sizeof ibhandlers[0]; + +static void +ib_dump_args(struct ibhandler *ih, struct ibarg *ap) +{ + + if (ih->name != NULL) + printf("%s(", ih->name); + else + printf("ibinvalid("); + printf("[0x%x]", ap->__field); + if (ap->__field & __F_HANDLE) printf(" handle=%d", ap->handle); + if (ap->__field & __F_EOS) printf(" eos=0x%x", ap->eos); + if (ap->__field & __F_EOT) printf(" eot=%d", ap->eot); + if (ap->__field & __F_TMO) printf(" tmo=%d", ap->tmo); + if (ap->__field & __F_PAD) printf(" pad=0x%x", ap->pad); + if (ap->__field & __F_SAD) printf(" sad=0x%x", ap->sad); + if (ap->__field & __F_BUFFER) printf(" buffer=%p", ap->buffer); + if (ap->__field & __F_CNT) printf(" cnt=%ld", ap->cnt); + if (ap->__field & __F_V) printf(" v=%d/0x%x", ap->v, ap->v); + /* XXX more ... */ + printf(")\n"); +} + +static int +gpib_ib_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct upd7210 *u; + struct ibfoo *ib; + int error = 0; + + u = dev->si_drv1; + + mtx_lock(&u->mutex); + if (u->busy) { + mtx_unlock(&u->mutex); + return (EBUSY); + } + u->busy = 1; + mtx_unlock(&u->mutex); + + if (u->dmachan >= 0) { + mtx_lock(&Giant); + error = isa_dma_acquire(u->dmachan); + if (!error) { + error = isa_dma_init(u->dmachan, PAGE_SIZE, M_WAITOK); + if (error) + isa_dma_release(u->dmachan); + } + mtx_unlock(&Giant); + } + + if (error) { + mtx_lock(&u->mutex); + u->busy = 0; + mtx_unlock(&u->mutex); + return (error); + } + + ib = malloc(sizeof *ib, M_IBFOO, M_WAITOK | M_ZERO); + LIST_INIT(&ib->handles); + callout_init(&ib->callout, 1); + ib->unrhdr = new_unrhdr(0, INT_MAX, NULL); + dev->si_drv2 = ib; + ib->u = u; + u->ibfoo = ib; + u->irq = gpib_ib_irq; + + upd7210_wr(u, AUXMR, AUXMR_CRST); + DELAY(10000); + DELAY(1000); + upd7210_wr(u, IMR1, 0x00); + upd7210_wr(u, IMR2, 0x00); + upd7210_wr(u, SPMR, 0x00); + upd7210_wr(u, ADR, 0x00); + upd7210_wr(u, ADR, ADR_ARS | ADR_DL | ADR_DT); + upd7210_wr(u, ADMR, ADMR_ADM0 | ADMR_TRM0 | ADMR_TRM1); + upd7210_wr(u, EOSR, 0x00); + upd7210_wr(u, AUXMR, C_ICR | 8); + upd7210_wr(u, AUXMR, C_PPR | PPR_U); + upd7210_wr(u, AUXMR, C_AUXA); + upd7210_wr(u, AUXMR, C_AUXB + 3); + upd7210_wr(u, AUXMR, C_AUXE + 0); + upd7210_wr(u, AUXMR, AUXMR_PON); + upd7210_wr(u, AUXMR, AUXMR_CIFC); + DELAY(100); + upd7210_wr(u, AUXMR, AUXMR_SIFC); + upd7210_wr(u, AUXMR, AUXMR_SREN); + return (0); +} + +static int +gpib_ib_close(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct upd7210 *u; + struct ibfoo *ib; + + u = dev->si_drv1; + ib = dev->si_drv2; + /* XXX: assert pointer consistency */ + + u->ibfoo = NULL; + /* XXX: free handles */ + dev->si_drv2 = NULL; + free(ib, M_IBFOO); + + if (u->dmachan >= 0) { + mtx_lock(&Giant); + isa_dma_release(u->dmachan); + mtx_unlock(&Giant); + } + mtx_lock(&u->mutex); + u->busy = 0; + ibdebug = 0; + upd7210_wr(u, IMR1, 0x00); + upd7210_wr(u, IMR2, 0x00); + upd7210_wr(u, AUXMR, AUXMR_CRST); + DELAY(10000); + mtx_unlock(&u->mutex); + return (0); +} + +static int +gpib_ib_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) +{ + struct ibarg *ap; + struct ibhandler *ih; + struct handle *h; + struct upd7210 *u; + struct ibfoo *ib; + int error; + struct timeval deadline, tv; + + u = dev->si_drv1; + ib = u->ibfoo; + + /* We only support a single ioctl, everything else is a mistake */ + if (cmd != GPIB_IBFOO) + return (ENOIOCTL); + + /* Check the identifier and field-bitmap in the arguments. */ + ap = (void *)data; + if (ap->__ident < 0 || ap->__ident >= max_ibhandler) + return (EINVAL); + ih = &ibhandlers[ap->__ident]; + if (ap->__field != ih->args) + return (EINVAL); + + if (ibdebug) + ib_dump_args(ih, ap); + + if (ih->func == NULL) + return (EOPNOTSUPP); + + ap->__iberr = 0; + ap->__ibsta = 0; + ap->__ibcnt = 0; + ap->retval = 0; + + if (ap->__field & __F_TMO) { + if (ap->tmo < 0 || ap->tmo >= max_timeouts) + return (ib_set_error(ap, EARG)); + } + + if (ap->__field & __F_EOS) { + if ((ap->eos & ~(REOS | XEOS | BIN | 0xff)) || + ((ap->eos & (BIN | 0x80)) == 0x80)) + return (ib_set_error(ap, EARG)); + } + if (ap->__field & __F_PAD) { + if (ap->pad < 0 || ap->pad > 30) + return (ib_set_error(ap, EARG)); + } + if (ap->__field & __F_SAD) { + if (ap->sad != 0 && (ap->sad < 0x60 || ap->sad > 126)) + return (ib_set_error(ap, EARG)); + } + + + mtx_lock(&u->mutex); + + + /* Find the handle, if any */ + h = NULL; + if ((ap->__field & __F_HANDLE) && gethandle(u, ap, &h)) { + mtx_unlock(&u->mutex); + return (0); + } + + /* Check that the handle is the right kind */ + if (h != NULL && !(h->kind & ih->kind)) { + mtx_unlock(&u->mutex); + return (ib_set_error(ap, EARG)); + } + + /* Set up handle and deadline */ + if (h != NULL && timevalisset(&h->timeout)) { + getmicrouptime(&deadline); + timevaladd(&deadline, &h->timeout); + } else { + timevalclear(&deadline); + } + + /* Wait for the card to be(come) available, respect deadline */ + while(u->busy != 1) { + error = msleep(ib, &u->mutex, + PZERO | PCATCH, "gpib_ibioctl", hz / 10); + if (error == 0) + continue; + mtx_unlock(&u->mutex); + if (error == EINTR) + return(ib_set_error(ap, EABO)); + if (error == EWOULDBLOCK && timevalisset(&deadline)) { + getmicrouptime(&tv); + if (timevalcmp(&deadline, &tv, <)) + return(ib_had_timeout(ap)); + } + mtx_lock(&u->mutex); + } + u->busy = 2; + mtx_unlock(&u->mutex); + + /* Hand over deadline handling to the callout routine */ + ib->ap = ap; + ib->h = h; + ib->mode = BUSY; + ib->deadline = deadline; + callout_reset(&ib->callout, hz / 5, gpib_ib_timeout, u); + + error = ih->func(ib); + + /* Release card */ + ib->mode = IDLE; + ib->ap = NULL; + ib->h = NULL; + timevalclear(&deadline); + callout_stop(&ib->callout); + + mtx_lock(&u->mutex); + u->busy = 1; + wakeup(ib); + mtx_unlock(&u->mutex); + + if (error) + return(ib_set_errno(ap, error)); + return (0); +} + +struct cdevsw gpib_ib_cdevsw = { + .d_version = D_VERSION, + .d_name = "gpib_ib", + .d_open = gpib_ib_open, + .d_ioctl = gpib_ib_ioctl, + .d_close = gpib_ib_close, +}; diff --git a/sys/dev/ieee488/ibfoo_int.h b/sys/dev/ieee488/ibfoo_int.h new file mode 100644 index 000000000000..28f72897a9df --- /dev/null +++ b/sys/dev/ieee488/ibfoo_int.h @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file defines the ABI between the userland gpib library and the + * kernel. This file should not be used anywhere else. + * + * $FreeBSD$ + */ + +#include <sys/ioccom.h> + +typedef void ibsrq_t(void); +enum ibfoo_id { + __ID_INVALID = 0, + __ID_IBASK, + __ID_IBBNA, + __ID_IBCAC, + __ID_IBCLR, + __ID_IBCMD, + __ID_IBCMDA, + __ID_IBCONFIG, + __ID_IBDEV, + __ID_IBDIAG, + __ID_IBDMA, + __ID_IBEOS, + __ID_IBEOT, + __ID_IBEVENT, + __ID_IBFIND, + __ID_IBGTS, + __ID_IBIST, + __ID_IBLINES, + __ID_IBLLO, + __ID_IBLN, + __ID_IBLOC, + __ID_IBONL, + __ID_IBPAD, + __ID_IBPCT, + __ID_IBPOKE, + __ID_IBPPC, + __ID_IBRD, + __ID_IBRDA, + __ID_IBRDF, + __ID_IBRDKEY, + __ID_IBRPP, + __ID_IBRSC, + __ID_IBRSP, + __ID_IBRSV, + __ID_IBSAD, + __ID_IBSGNL, + __ID_IBSIC, + __ID_IBSRE, + __ID_IBSRQ, + __ID_IBSTOP, + __ID_IBTMO, + __ID_IBTRAP, + __ID_IBTRG, + __ID_IBWAIT, + __ID_IBWRT, + __ID_IBWRTA, + __ID_IBWRTF, + __ID_IBWRTKEY, + __ID_IBXTRC +}; + +#define __F_HANDLE (1 << 0) +#define __F_SPR (1 << 1) +#define __F_BUFFER (1 << 2) +#define __F_RETVAL (1 << 3) +#define __F_BDNAME (1 << 4) +#define __F_MASK (1 << 5) +#define __F_PADVAL (1 << 6) +#define __F_SADVAL (1 << 7) +#define __F_CNT (1 << 8) +#define __F_TMO (1 << 9) +#define __F_EOS (1 << 10) +#define __F_PPR (1 << 11) +#define __F_EOT (1 << 12) +#define __F_V (1 << 13) +#define __F_VALUE (1 << 14) +#define __F_SAD (1 << 15) +#define __F_BOARDID (1 << 16) +#define __F_OPTION (1 << 17) +#define __F_FLNAME (1 << 18) +#define __F_FUNC (1 << 19) +#define __F_LINES (1 << 20) +#define __F_PAD (1 << 21) +#define __F_MODE (1 << 22) +#define __F_LISTENFLAG (1 << 23) +#define __F_EVENT (1 << 24) + +struct ibarg { + enum ibfoo_id __ident; + unsigned int __field; + int __retval; + int __ibsta; + int __iberr; + int __ibcnt; + int handle; + char * spr; + void * buffer; + int * retval; + char * bdname; + int mask; + int padval; + int sadval; + long cnt; + int tmo; + int eos; + char * ppr; + int eot; + int v; + int value; + int sad; + int boardID; + int option; + char * flname; + ibsrq_t * func; + short * lines; + int pad; + int mode; + short * listenflag; + short * event; +}; + +#define GPIB_IBFOO _IOWR(4, 0, struct ibarg) diff --git a/sys/dev/ieee488/pcii.c b/sys/dev/ieee488/pcii.c new file mode 100644 index 000000000000..d575f6927d59 --- /dev/null +++ b/sys/dev/ieee488/pcii.c @@ -0,0 +1,192 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Driver for GPIB cards based on NEC µPD7210 and compatibles. + * + * This driver just hooks up to the hardware and leaves all the interesting + * stuff to upd7210.c. + * + * Supported hardware: + * PCIIA compatible cards. + * + * Tested and known working: + * "B&C Microsystems PC488A-0" + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> +#include <isa/isavar.h> + +#define UPD7210_HW_DRIVER +#include <dev/ieee488/upd7210.h> + +struct pcii_softc { + int foo; + struct resource *port[8]; + struct resource *irq; + struct resource *dma; + void *intr_handler; + struct upd7210 upd7210; +}; + +static devclass_t pcii_devclass; + +static int pcii_probe(device_t dev); +static int pcii_attach(device_t dev); + +static device_method_t pcii_methods[] = { + DEVMETHOD(device_probe, pcii_probe), + DEVMETHOD(device_attach, pcii_attach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + { 0, 0 } +}; + +static driver_t pcii_driver = { + "pcii", + pcii_methods, + sizeof(struct pcii_softc), +}; + +static int +pcii_probe(device_t dev) +{ + struct resource *port; + int rid; + u_long start, count; + int i, j, error = 0; + + device_set_desc(dev, "PCII IEEE-4888 controller"); + + rid = 0; + if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count) != 0) + return ENXIO; + if ((start & 0x3ff) != 0x2e1) + return (ENXIO); + count = 1; + if (bus_set_resource(dev, SYS_RES_IOPORT, rid, start, count) != 0) + return ENXIO; + for (i = 0; i < 8; i++) { + j = bus_set_resource(dev, SYS_RES_IOPORT, i, + start + 0x400 * i, 1); + if (j) { + error = ENXIO; + break; + } + rid = i; + port = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &rid, RF_ACTIVE); + if (port == NULL) + return (ENXIO); + else + bus_release_resource(dev, SYS_RES_IOPORT, i, port); + } + + rid = 0; + port = bus_alloc_resource_any(dev, + SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); + if (port == NULL) + return (ENXIO); + bus_release_resource(dev, SYS_RES_IRQ, rid, port); + + return (error); +} + +static int +pcii_attach(device_t dev) +{ + struct pcii_softc *sc; + int unit; + int rid; + int i, error = 0; + + unit = device_get_unit(dev); + sc = device_get_softc(dev); + memset(sc, 0, sizeof *sc); + + device_set_desc(dev, "PCII IEEE-4888 controller"); + + for (rid = 0; rid < 8; rid++) { + sc->port[rid] = bus_alloc_resource_any(dev, + SYS_RES_IOPORT, &rid, RF_ACTIVE); + if (sc->port[rid] == NULL) { + error = ENXIO; + break; + } + sc->upd7210.reg_tag[rid] = rman_get_bustag(sc->port[rid]); + sc->upd7210.reg_handle[rid] = rman_get_bushandle(sc->port[rid]); + } + if (!error) { + rid = 0; + sc->irq = bus_alloc_resource_any(dev, + SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); + if (sc->irq == NULL) { + error = ENXIO; + } else { + error = bus_setup_intr(dev, sc->irq, + INTR_TYPE_MISC | INTR_MPSAFE, + upd7210intr, &sc->upd7210, &sc->intr_handler); + } + } + if (!error) { + rid = 0; + sc->dma = bus_alloc_resource_any(dev, + SYS_RES_DRQ, &rid, RF_ACTIVE | RF_SHAREABLE); + if (sc->dma == NULL) + sc->upd7210.dmachan = -1; + else + sc->upd7210.dmachan = rman_get_start(sc->dma); + } + if (error) { + for (i = 0; i < 8; i++) { + if (sc->port[i] == NULL) + break; + bus_release_resource(dev, SYS_RES_IOPORT, + 0, sc->port[i]); + } + if (sc->intr_handler != NULL) + bus_teardown_intr(dev, sc->irq, sc->intr_handler); + if (sc->irq != NULL) + bus_release_resource(dev, SYS_RES_IRQ, i, sc->irq); + } + upd7210attach(&sc->upd7210); + return (error); +} + +DRIVER_MODULE(pcii, isa, pcii_driver, pcii_devclass, 0, 0); +DRIVER_MODULE(pcii, acpi, pcii_driver, pcii_devclass, 0, 0); diff --git a/sys/dev/ieee488/ugpib.h b/sys/dev/ieee488/ugpib.h new file mode 100644 index 000000000000..726a70219dd8 --- /dev/null +++ b/sys/dev/ieee488/ugpib.h @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef _DEV_IEEE488_UGPIB_H_ +#define _DEV_IEEE488_UGPIB_H_ + +/* ibfoo() return values */ +#define EDVR 0 /* System error */ +#define ECIC 1 /* Not Active Controller */ +#define ENOL 2 /* Nobody listening */ +#define EADR 3 /* Controller not addressed */ +#define EARG 4 /* Invalid argument */ +#define ESAC 5 /* Not System Controller */ +#define EABO 6 /* I/O Aborted/Time out */ +#define ENEB 7 /* No such controller */ +#define EOIP 10 /* Async I/O in progress */ +#define ECAP 11 /* No such capability */ +#define EFSO 12 /* File system error */ +#define EBUS 14 /* Command byte xfer error */ +#define ESTB 15 /* Serial poll status byte lost */ +#define ESRQ 16 /* SRQ line stuck */ +#define ETAB 20 /* Table problem */ + +/* ibsta bits */ +#define ERR (1<<15) /* Error */ +#define TIMO (1<<14) /* Timeout */ +#define END (1<<13) /* EOI/EOS */ +#define SRQI (1<<12) /* SRQ */ +#define RQS (1<<11) /* Device requests service */ +#define SPOLL (1<<10) /* Serial Poll */ +#define EVENT (1<<9) /* Event occured */ +#define CMPL (1<<8) /* I/O complete */ +#define LOK (1<<7) /* Lockout */ +#define REM (1<<6) /* Remote */ +#define CIC (1<<5) /* CIC */ +#define ATN (1<<4) /* ATN */ +#define TACS (1<<3) /* Talker */ +#define LACS (1<<2) /* Listener */ +#define DTAS (1<<1) /* Device trigger status */ +#define DCAS (1<<0) /* Device clear state */ + +/* Timeouts */ +#define TNONE 0 +#define T10us 1 +#define T30us 2 +#define T100us 3 +#define T300us 4 +#define T1ms 5 +#define T3ms 6 +#define T10ms 7 +#define T30ms 8 +#define T100ms 9 +#define T300ms 10 +#define T1s 11 +#define T3s 12 +#define T10s 13 +#define T30s 14 +#define T100s 15 +#define T300s 16 +#define T1000s 17 + +/* EOS bits */ +#define REOS (1 << 10) +#define XEOS (1 << 11) +#define BIN (1 << 12) + +/* Bus commands */ +#define GTL 0x01 /* Go To Local */ +#define SDC 0x04 /* Selected Device Clear */ +#define GET 0x08 /* Group Execute Trigger */ +#define LAD 0x20 /* Listen address */ +#define UNL 0x3F /* Unlisten */ +#define TAD 0x40 /* Talk address */ +#define UNT 0x5F /* Untalk */ + +#ifndef _KERNEL + +extern int ibcnt, iberr, ibsta; + +int ibask(int handle, int option, int *retval); +int ibbna(int handle, char *bdname); +int ibcac(int handle, int v); +int ibclr(int handle); +int ibcmd(int handle, void *buffer, long cnt); +int ibcmda(int handle, void *buffer, long cnt); +int ibconfig(int handle, int option, int value); +int ibdev(int boardID, int pad, int sad, int tmo, int eot, int eos); +int ibdiag(int handle, void *buffer, long cnt); +int ibdma(int handle, int v); +int ibeos(int handle, int eos); +int ibeot(int handle, int eot); +int ibevent(int handle, short *event); +int ibfind(char *bdname); +int ibgts(int handle, int v); +int ibist(int handle, int v); +int iblines(int handle, short *lines); +int ibllo(int handle); +int ibln(int handle, int padval, int sadval, short *listenflag); +int ibloc(int handle); +int ibonl(int handle, int v); +int ibpad(int handle, int pad); +int ibpct(int handle); +int ibpoke(int handle, int option, int value); +int ibppc(int handle, int v); +int ibrd(int handle, void *buffer, long cnt); +int ibrda(int handle, void *buffer, long cnt); +int ibrdf(int handle, char *flname); +int ibrdkey(int handle, void *buffer, int cnt); +int ibrpp(int handle, char *ppr); +int ibrsc(int handle, int v); +int ibrsp(int handle, char *spr); +int ibrsv(int handle, int v); +int ibsad(int handle, int sad); +int ibsgnl(int handle, int v); +int ibsic(int handle); +int ibsre(int handle, int v); +int ibsrq(void (*func)(void)); +int ibstop(int handle); +int ibtmo(int handle, int tmo); +int ibtrap(int mask, int mode); +int ibtrg(int handle); +int ibwait(int handle, int mask); +int ibwrt(int handle, const void *buffer, long cnt); +int ibwrta(int handle, const void *buffer, long cnt); +int ibwrtf(int handle, const char *flname); +int ibwrtkey(int handle, const void *buffer, int cnt); +int ibxtrc(int handle, void *buffer, long cnt); +#endif /* _KERNEL */ +#endif /* _DEV_IEEE488_UGPIB_H_ */ diff --git a/sys/dev/ieee488/upd7210.c b/sys/dev/ieee488/upd7210.c new file mode 100644 index 000000000000..e4b50ace490a --- /dev/null +++ b/sys/dev/ieee488/upd7210.c @@ -0,0 +1,291 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * High-level driver for µPD7210 based GPIB cards. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +# define GPIB_DEBUG +# undef GPIB_DEBUG + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/limits.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <isa/isavar.h> + +#define UPD7210_HW_DRIVER +#define UPD7210_SW_DRIVER +#include <dev/ieee488/upd7210.h> + +static MALLOC_DEFINE(M_GPIB, "GPIB", "GPIB"); + +/* upd7210 generic stuff */ + +void +upd7210_print_isr(u_int isr1, u_int isr2) +{ + printf("isr1=0x%b isr2=0x%b", + isr1, "\20\10CPT\7APT\6DET\5ENDRX\4DEC\3ERR\2DO\1DI", + isr2, "\20\10INT\7SRQI\6LOK\5REM\4CO\3LOKC\2REMC\1ADSC"); +} + +u_int +upd7210_rd(struct upd7210 *u, enum upd7210_rreg reg) +{ + u_int r; + + r = bus_space_read_1( + u->reg_tag[reg], + u->reg_handle[reg], + u->reg_offset[reg]); + u->rreg[reg] = r; + return (r); +} + +void +upd7210_wr(struct upd7210 *u, enum upd7210_wreg reg, u_int val) +{ + bus_space_write_1( + u->reg_tag[reg], + u->reg_handle[reg], + u->reg_offset[reg], val); + u->wreg[reg] = val; + if (reg == AUXMR) + u->wreg[8 + (val >> 5)] = val & 0x1f; +} + +void +upd7210intr(void *arg) +{ + u_int isr1, isr2; + struct upd7210 *u; + + u = arg; + mtx_lock(&u->mutex); + isr1 = upd7210_rd(u, ISR1); + isr2 = upd7210_rd(u, ISR2); + if (u->busy == 0 || u->irq == NULL || !u->irq(u, 1)) { + printf("upd7210intr [%02x %02x %02x", + upd7210_rd(u, DIR), isr1, isr2); + printf(" %02x %02x %02x %02x %02x] ", + upd7210_rd(u, SPSR), + upd7210_rd(u, ADSR), + upd7210_rd(u, CPTR), + upd7210_rd(u, ADR0), + upd7210_rd(u, ADR1)); + upd7210_print_isr(isr1, isr2); + printf("\n"); + } + mtx_unlock(&u->mutex); +} + +int +upd7210_take_ctrl_async(struct upd7210 *u) +{ + int i; + + upd7210_wr(u, AUXMR, AUXMR_TCA); + + if (!(upd7210_rd(u, ADSR) & ADSR_ATN)) + return (0); + for (i = 0; i < 20; i++) { + DELAY(1); + if (!(upd7210_rd(u, ADSR) & ADSR_ATN)) + return (0); + } + return (1); +} + +int +upd7210_goto_standby(struct upd7210 *u) +{ + int i; + + upd7210_wr(u, AUXMR, AUXMR_GTS); + + if (upd7210_rd(u, ADSR) & ADSR_ATN) + return (0); + for (i = 0; i < 20; i++) { + DELAY(1); + if (upd7210_rd(u, ADSR) & ADSR_ATN) + return (0); + } + return (1); +} + +/* Unaddressed Listen Only mode */ + +static int +gpib_l_irq(struct upd7210 *u, int intr __unused) +{ + int i; + + if (u->rreg[ISR1] & 1) { + i = upd7210_rd(u, DIR); + u->buf[u->buf_wp++] = i; + u->buf_wp &= (u->bufsize - 1); + i = (u->buf_rp + u->bufsize - u->buf_wp) & (u->bufsize - 1); + if (i < 8) + upd7210_wr(u, IMR1, 0); + wakeup(u->buf); + return (1); + } + return (0); +} + +static int +gpib_l_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct upd7210 *u; + + u = dev->si_drv1; + + mtx_lock(&u->mutex); + if (u->busy) { + mtx_unlock(&u->mutex); + return (EBUSY); + } + u->busy = 1; + u->irq = gpib_l_irq; + mtx_unlock(&u->mutex); + + u->buf = malloc(PAGE_SIZE, M_GPIB, M_WAITOK); + u->bufsize = PAGE_SIZE; + u->buf_wp = 0; + u->buf_rp = 0; + + upd7210_wr(u, AUXMR, AUXMR_CRST); + DELAY(10000); + upd7210_wr(u, AUXMR, C_ICR | 8); + DELAY(1000); + upd7210_wr(u, ADR, 0x60); + upd7210_wr(u, ADR, 0xe0); + upd7210_wr(u, ADMR, 0x70); + upd7210_wr(u, AUXMR, AUXMR_PON); + upd7210_wr(u, IMR1, 0x01); + return (0); +} + +static int +gpib_l_close(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct upd7210 *u; + + u = dev->si_drv1; + + mtx_lock(&u->mutex); + u->busy = 0; + upd7210_wr(u, AUXMR, AUXMR_CRST); + DELAY(10000); + upd7210_wr(u, IMR1, 0x00); + upd7210_wr(u, IMR2, 0x00); + free(u->buf, M_GPIB); + u->buf = NULL; + mtx_unlock(&u->mutex); + return (0); +} + +static int +gpib_l_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct upd7210 *u; + int error; + size_t z; + + u = dev->si_drv1; + error = 0; + + mtx_lock(&u->mutex); + while (u->buf_wp == u->buf_rp) { + error = msleep(u->buf, &u->mutex, PZERO | PCATCH, + "gpibrd", hz); + if (error && error != EWOULDBLOCK) { + mtx_unlock(&u->mutex); + return (error); + } + } + while (uio->uio_resid > 0 && u->buf_wp != u->buf_rp) { + if (u->buf_wp < u->buf_rp) + z = u->bufsize - u->buf_rp; + else + z = u->buf_wp - u->buf_rp; + if (z > uio->uio_resid) + z = uio->uio_resid; + mtx_unlock(&u->mutex); + error = uiomove(u->buf + u->buf_rp, z, uio); + mtx_lock(&u->mutex); + if (error) + break; + u->buf_rp += z; + u->buf_rp &= (u->bufsize - 1); + } + if (u->wreg[IMR1] == 0) + upd7210_wr(u, IMR1, 0x01); + mtx_unlock(&u->mutex); + return (error); +} + +static struct cdevsw gpib_l_cdevsw = { + .d_version = D_VERSION, + .d_name = "gpib_l", + .d_open = gpib_l_open, + .d_close = gpib_l_close, + .d_read = gpib_l_read, +}; + +/* Housekeeping */ + +void +upd7210attach(struct upd7210 *u) +{ + int unit = 0; + struct cdev *dev; + + mtx_init(&u->mutex, "gpib", NULL, MTX_DEF); + u->cdev = make_dev(&gpib_l_cdevsw, unit, + UID_ROOT, GID_WHEEL, 0444, + "gpib%ul", unit); + u->cdev->si_drv1 = u; + + dev = make_dev(&gpib_ib_cdevsw, unit, + UID_ROOT, GID_WHEEL, 0444, + "gpib%uib", unit); + dev->si_drv1 = u; + dev_depends(u->cdev, dev); +} diff --git a/sys/dev/ieee488/upd7210.h b/sys/dev/ieee488/upd7210.h new file mode 100644 index 000000000000..7ba0f8eb8d10 --- /dev/null +++ b/sys/dev/ieee488/upd7210.h @@ -0,0 +1,234 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * Locating an actual µPD7210 data book has proven quite impossible for me. + * There are a fair number of newer chips which are supersets of the µPD7210 + * but they are particular eager to comprehensively mark what the extensions + * are and what is in the base set. Some even give the registers and their + * bits new names. + * + * The following information is based on a description of the µPD7210 found + * in an old manual for a VME board which used the chip. + */ + +#ifndef _DEV_IEEE488_UPD7210_H_ +#define _DEV_IEEE488_UPD7210_H_ +#ifdef _KERNEL + +struct upd7210; +struct ibfoo; + +/* upd7210 interface definitions for HW drivers */ + +typedef int upd7210_irq_t(struct upd7210 *, int); + +struct upd7210 { + bus_space_handle_t reg_handle[8]; + bus_space_tag_t reg_tag[8]; + u_int reg_offset[8]; + int dmachan; + + /* private stuff */ + struct mtx mutex; + uint8_t rreg[8]; + uint8_t wreg[8 + 8]; + + upd7210_irq_t *irq; + + int busy; + u_char *buf; + size_t bufsize; + u_int buf_wp; + u_int buf_rp; + struct cdev *cdev; + + struct ibfoo *ibfoo; +}; + +#ifdef UPD7210_HW_DRIVER +void upd7210intr(void *); +void upd7210attach(struct upd7210 *); +#endif + +#ifdef UPD7210_SW_DRIVER + +/* upd7210 hardware definitions. */ + +/* Write registers */ +enum upd7210_wreg { + CDOR = 0, /* Command/Data Out Register */ + IMR1 = 1, /* Interrupt Mask Register 1 */ + IMR2 = 2, /* Interrupt Mask Register 2 */ + SPMR = 3, /* Serial Poll Mode Register */ + ADMR = 4, /* ADdress Mode Register */ + AUXMR = 5, /* AUXilliary Mode Register */ + ICR = 5, /* Internal Counter Register */ + PPR = 5, /* Parallel Poll Register */ + AUXRA = 5, /* AUXilliary Register A */ + AUXRB = 5, /* AUXilliary Register B */ + AUXRE = 5, /* AUXilliary Register E */ + ADR = 6, /* ADdress Register */ + EOSR = 7, /* End-Of-String Register */ +}; + +/* Read registers */ +enum upd7210_rreg { + DIR = 0, /* Data In Register */ + ISR1 = 1, /* Interrupt Status Register 1 */ + ISR2 = 2, /* Interrupt Status Register 2 */ + SPSR = 3, /* Serial Poll Status Register */ + ADSR = 4, /* ADdress Status Register */ + CPTR = 5, /* Command Pass Though Register */ + ADR0 = 6, /* ADdress Register 0 */ + ADR1 = 7, /* ADdress Register 1 */ +}; + +/* Bits for ISR1 and IMR1 */ +#define IXR1_DI (1 << 0) /* Data In */ +#define IXR1_DO (1 << 1) /* Data Out */ +#define IXR1_ERR (1 << 2) /* Error */ +#define IXR1_DEC (1 << 3) /* Device Clear */ +#define IXR1_ENDRX (1 << 4) /* End Received */ +#define IXR1_DET (1 << 5) /* Device Execute Trigger */ +#define IXR1_APT (1 << 6) /* Address Pass-Through */ +#define IXR1_CPT (1 << 7) /* Command Pass-Through */ + +/* Bits for ISR2 and IMR2 */ +#define IXR2_ADSC (1 << 0) /* Addressed Status Change */ +#define IXR2_REMC (1 << 1) /* Remote Change */ +#define IXR2_LOKC (1 << 2) /* Lockout Change */ +#define IXR2_CO (1 << 3) /* Command Out */ +#define ISR2_REM (1 << 4) /* Remove */ +#define IMR2_DMAI (1 << 4) /* DMA In Enable */ +#define ISR2_LOK (1 << 5) /* Lockout */ +#define IMR2_DMAO (1 << 5) /* DMA Out Enable */ +#define IXR2_SRQI (1 << 6) /* Service Request Input */ +#define ISR2_INT (1 << 7) /* Interrupt */ + +#define SPSR_PEND (1 << 6) /* Pending */ +#define SPMR_RSV (1 << 6) /* Request SerVice */ + +#define ADSR_MJMN (1 << 0) /* MaJor MiNor */ +#define ADSR_TA (1 << 1) /* Talker Active */ +#define ADSR_LA (1 << 2) /* Listener Active */ +#define ADSR_TPAS (1 << 3) /* Talker Primary Addr. State */ +#define ADSR_LPAS (1 << 4) /* Listener Primary Addr. State */ +#define ADSR_SPMS (1 << 5) /* Serial Poll Mode State */ +#define ADSR_ATN (1 << 6) /* Attention */ +#define ADSR_CIC (1 << 7) /* Controller In Charge */ + +#define ADMR_ADM0 (1 << 0) /* Address Mode 0 */ +#define ADMR_ADM1 (1 << 1) /* Address Mode 1 */ +#define ADMR_TRM0 (1 << 4) /* Transmit/Receive Mode 0 */ +#define ADMR_TRM1 (1 << 5) /* Transmit/Receive Mode 1 */ +#define ADMR_LON (1 << 6) /* Listen Only */ +#define ADMR_TON (1 << 7) /* Talk Only */ + +/* Constant part of overloaded write registers */ +#define C_ICR 0x20 +#define C_PPR 0x60 +#define C_AUXA 0x80 +#define C_AUXB 0xa0 +#define C_AUXE 0xc0 + +#define AUXMR_PON 0x00 /* Immediate Execute pon */ +#define AUXMR_CPP 0x01 /* Clear Parallel Poll */ +#define AUXMR_CRST 0x02 /* Chip Reset */ +#define AUXMR_RFD 0x03 /* Finish Handshake */ +#define AUXMR_TRIG 0x04 /* Trigger */ +#define AUXMR_RTL 0x05 /* Return to local */ +#define AUXMR_SEOI 0x06 /* Send EOI */ +#define AUXMR_NVSA 0x07 /* Non-Valid Secondary cmd/addr */ + /* 0x08 undefined/unknown */ +#define AUXMR_SPP 0x09 /* Set Parallel Poll */ + /* 0x0a undefined/unknown */ + /* 0x0b undefined/unknown */ + /* 0x0c undefined/unknown */ + /* 0x0d undefined/unknown */ + /* 0x0e undefined/unknown */ +#define AUXMR_VSA 0x0f /* Valid Secondary cmd/addr */ +#define AUXMR_GTS 0x10 /* Go to Standby */ +#define AUXMR_TCA 0x11 /* Take Control Async (pulsed) */ +#define AUXMR_TCS 0x12 /* Take Control Synchronously */ +#define AUXMR_LISTEN 0x13 /* Listen */ +#define AUXMR_DSC 0x14 /* Disable System Control */ + /* 0x15 undefined/unknown */ +#define AUXMR_SIFC 0x16 /* Set IFC */ +#define AUXMR_CREN 0x17 /* Clear REN */ + /* 0x18 undefined/unknown */ + /* 0x19 undefined/unknown */ +#define AUXMR_TCSE 0x1a /* Take Control Sync on End */ +#define AUXMR_LCM 0x1b /* Listen Continuously Mode */ +#define AUXMR_LUNL 0x1c /* Local Unlisten */ +#define AUXMR_EPP 0x1d /* Execute Parallel Poll */ +#define AUXMR_CIFC 0x1e /* Clear IFC */ +#define AUXMR_SREN 0x1f /* Set REN */ + +#define PPR_U (1 << 4) /* Unconfigure */ +#define PPR_S (1 << 3) /* Status Polarity */ + +#define AUXA_HLDA (1 << 0) /* Holdoff on All */ +#define AUXA_HLDE (1 << 1) /* Holdoff on END */ +#define AUXA_REOS (1 << 2) /* End on EOS received */ +#define AUXA_XEOS (1 << 3) /* Transmit END with EOS */ +#define AUXA_BIN (1 << 4) /* Binary */ + +#define AUXB_CPTE (1 << 0) /* Cmd Pass Through Enable */ +#define AUXB_SPEOI (1 << 1) /* Send Serial Poll EOI */ +#define AUXB_TRI (1 << 2) /* Three-State Timing */ +#define AUXB_INV (1 << 3) /* Invert */ +#define AUXB_ISS (1 << 4) /* Individual Status Select */ + +#define AUXE_DHDT (1 << 0) /* DAC Holdoff on DTAS */ +#define AUXE_DHDC (1 << 1) /* DAC Holdoff on DCAS */ + +#define ADR0_DL0 (1 << 5) /* Disable Listener 0 */ +#define ADR0_DT0 (1 << 6) /* Disable Talker 0 */ + +#define ADR_DL (1 << 5) /* Disable Listener */ +#define ADR_DT (1 << 6) /* Disable Talker */ +#define ADR_ARS (1 << 7) /* Address Register Select */ + +#define ADR1_DL1 (1 << 5) /* Disable Listener 1 */ +#define ADR1_DT1 (1 << 6) /* Disable Talker 1 */ +#define ADR1_EOI (1 << 7) /* End or Identify */ + +/* Stuff from software drivers */ +extern struct cdevsw gpib_l_cdevsw; +extern struct cdevsw gpib_ib_cdevsw; + +/* Stuff from upd7210.c */ +void upd7210_print_isr(u_int isr1, u_int isr2); +u_int upd7210_rd(struct upd7210 *u, enum upd7210_rreg reg); +void upd7210_wr(struct upd7210 *u, enum upd7210_wreg reg, u_int val); +int upd7210_take_ctrl_async(struct upd7210 *u); +int upd7210_goto_standby(struct upd7210 *u); + +#endif /* UPD7210_SW_DRIVER */ + +#endif /* _KERNEL */ +#endif /* _DEV_IEEE488_UPD7210_H_ */ |
