diff options
author | phk <phk@FreeBSD.org> | 2005-02-06 15:22:23 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2005-02-06 15:22:23 +0000 |
commit | cbedd686e72f6cc55f80b962e4199c78380b1b30 (patch) | |
tree | 99dcd1fad2091bdceddcf924c1c549f407478a56 /sys/dev | |
parent | bd8993edc121200fd4d77aa1cb5073109d702b0d (diff) | |
download | FreeBSD-src-cbedd686e72f6cc55f80b962e4199c78380b1b30.zip FreeBSD-src-cbedd686e72f6cc55f80b962e4199c78380b1b30.tar.gz |
Further elaborate the GPIB driver. We now support a minimal subset of
the ibfoo() API.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ieee488/ibfoo_int.h | 147 | ||||
-rw-r--r-- | sys/dev/ieee488/pcii.c | 294 | ||||
-rw-r--r-- | sys/dev/ieee488/ugpib.h | 152 | ||||
-rw-r--r-- | sys/dev/ieee488/upd7210.c | 1026 | ||||
-rw-r--r-- | sys/dev/ieee488/upd7210.h | 213 |
5 files changed, 1569 insertions, 263 deletions
diff --git a/sys/dev/ieee488/ibfoo_int.h b/sys/dev/ieee488/ibfoo_int.h new file mode 100644 index 0000000..845bb7f --- /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 ibfoo_iocarg { + enum ibfoo_id __ident; + unsigned int __field; + int __retval; + int __ibsts; + 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 ibfoo_iocarg) diff --git a/sys/dev/ieee488/pcii.c b/sys/dev/ieee488/pcii.c index fadd96c..ab59545 100644 --- a/sys/dev/ieee488/pcii.c +++ b/sys/dev/ieee488/pcii.c @@ -6,22 +6,27 @@ * 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 as - * the first lines of this file unmodified. + * 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 AUTHORS ``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 AUTHORS 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 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. @@ -29,13 +34,6 @@ * Tested and known working: * "B&C Microsystems PC488A-0" * - * A whole lot of wonderful things could be written for GPIB, but for now - * I have just written it such that it is possible to capture data in the - * mode known as "unaddressed listen only mode". This is what many plotters - * and printers do on GPIB. This is enough to capture some output from - * various test instruments. - * - * If you are interested in working on this, send me email. */ #include <sys/cdefs.h> @@ -43,53 +41,27 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> -#include <sys/conf.h> -#include <sys/malloc.h> +#include <sys/lock.h> +#include <sys/mutex.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/bus.h> -#include <sys/mutex.h> -#include <sys/uio.h> #include <machine/bus.h> #include <machine/resource.h> #include <sys/rman.h> +#include <isa/isavar.h> -/* ---> upd7210.h at some point. */ - -struct upd7210 { - bus_space_handle_t reg_handle[8]; - bus_space_tag_t reg_tag[8]; - u_int reg_offset[8]; - - /* private stuff */ - struct mtx mutex; - uint8_t rreg[8]; - uint8_t wreg[8]; - - int busy; - u_char *buf; - size_t bufsize; - u_int buf_wp; - u_int buf_rp; - struct cdev *cdev; -}; - -static void upd7210intr(void *); -static void upd7210attach(struct upd7210 *); - - -/* ----> pcii.c */ +#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; }; -#define HERE() printf("pcii HERE %s:%d\n", __FILE__, __LINE__) - static devclass_t pcii_devclass; static int pcii_probe(device_t dev); @@ -190,8 +162,16 @@ pcii_attach(device_t dev) 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) { -device_printf(dev, "error = %d\n", error); for (i = 0; i < 8; i++) { if (sc->port[i] == NULL) break; @@ -209,215 +189,3 @@ device_printf(dev, "error = %d\n", error); DRIVER_MODULE(pcii, isa, pcii_driver, pcii_devclass, 0, 0); DRIVER_MODULE(pcii, acpi, pcii_driver, pcii_devclass, 0, 0); - -/* ---> upd7210.c at some point */ - -enum upd7210_wreg { - CDOR = 0, /* Command/data out */ - IMR1 = 1, /* Interrupt mask 1 */ - IMR2 = 2, /* Interrupt mask 2 */ - SPMR = 3, /* Serial poll mode */ - ADMR = 4, /* Address mode */ - AUXMR = 5, /* Auxilliary mode */ - ADR = 6, /* Address */ - EOSR = 7, /* End-of-string */ -}; - -enum upd7210_rreg { - DIR = 0, /* Data in */ - ISR1 = 1, /* Interrupt status 1 */ - ISR2 = 2, /* Interrupt status 2 */ - SPSR = 3, /* Serial poll status */ - ADSR = 4, /* Address status */ - CPTR = 5, /* Command pass though */ - ADR0 = 6, /* Address 1 */ - ADR1 = 7, /* Address 2 */ -}; - -#define AUXMR_PON 0x00 -#define AUXMR_CRST 0x02 -#define AUXMR_RFD 0x03 -#define AUXMR_SEOI 0x06 -#define AUXMR_GTS 0x10 -#define AUXMR_TCA 0x11 -#define AUXMR_TCS 0x12 -#define AUXMR_TCSE 0x1a -#define AUXMR_DSC 0x14 -#define AUXMR_CIFC 0x16 -#define AUXMR_SIFC 0x1e -#define AUXMR_CREN 0x17 -#define AUXMR_SREN 0x1f -#define AUXMR_ICTR 0x20 -#define AUXMR_PPR 0x60 -#define AUXMR_RA 0x80 -#define AUXMR_RB 0xa0 -#define AUXMR_RE 0xc0 - - -/* upd7210 generic stuff */ - -static u_int -read_reg(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); -} - -static void -write_reg(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; -} - -static void -upd7210intr(void *arg) -{ - int i; - u_int isr1, isr2; - struct upd7210 *u; - - u = arg; - mtx_lock(&u->mutex); - isr1 = read_reg(u, ISR1); - isr2 = read_reg(u, ISR2); - if (isr1 & 1) { - i = read_reg(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) - write_reg(u, IMR1, 0); - wakeup(u->buf); - } else { - printf("upd7210intr [%02x %02x %02x", - read_reg(u, DIR), isr1, isr2); - printf(" %02x %02x %02x %02x %02x]\n", - read_reg(u, SPSR), - read_reg(u, ADSR), - read_reg(u, CPTR), - read_reg(u, ADR0), - read_reg(u, ADR1)); - } - mtx_unlock(&u->mutex); -} - -static int -gpib_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) - return (EBUSY); - u->busy = 1; - mtx_unlock(&u->mutex); - - u->buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK); - u->bufsize = PAGE_SIZE; - u->buf_wp = 0; - u->buf_rp = 0; - - write_reg(u, AUXMR, AUXMR_CRST); - DELAY(10000); - write_reg(u, AUXMR, AUXMR_ICTR | 8); - DELAY(1000); - write_reg(u, ADR, 0x60); - write_reg(u, ADR, 0xe0); - write_reg(u, ADMR, 0x70); - write_reg(u, AUXMR, AUXMR_PON); - write_reg(u, IMR1, 0x01); - return (0); -} - -static int -gpib_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; - write_reg(u, AUXMR, AUXMR_CRST); - DELAY(10000); - write_reg(u, IMR1, 0x00); - write_reg(u, IMR2, 0x00); - free(u->buf, M_DEVBUF); - u->buf = NULL; - mtx_unlock(&u->mutex); - return (0); -} - -static int -gpib_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) - write_reg(u, IMR1, 0x01); - mtx_unlock(&u->mutex); - return (error); -} - - - -struct cdevsw gpib_cdevsw = { - .d_version = D_VERSION, - .d_name = "gpib", - .d_open = gpib_open, - .d_close = gpib_close, - .d_read = gpib_read, -}; - -static void -upd7210attach(struct upd7210 *u) -{ - int unit = 0; - - mtx_init(&u->mutex, "gpib", NULL, MTX_DEF); - u->cdev = make_dev(&gpib_cdevsw, unit, - UID_ROOT, GID_WHEEL, 0444, - "gpib%ul", unit); - u->cdev->si_drv1 = u; -} diff --git a/sys/dev/ieee488/ugpib.h b/sys/dev/ieee488/ugpib.h new file mode 100644 index 0000000..77b7fcd --- /dev/null +++ b/sys/dev/ieee488/ugpib.h @@ -0,0 +1,152 @@ +/*- + * 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 LAD 0x20 /* Listen address */ +#define UNL 0x3F /* Unlisten */ +#define TAD 0x40 /* Talk address */ +#define UNT 0x5F /* Untalk */ + +#ifndef _KERNEL + +extern int ibcnt, iberr; + +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 v); +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 v); +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 v); +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, void *buffer, long cnt); +int ibwrta(int handle, void *buffer, long cnt); +int ibwrtf(int handle, char *flname); +int ibwrtkey(int handle, 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 0000000..4a1c8524 --- /dev/null +++ b/sys/dev/ieee488/upd7210.c @@ -0,0 +1,1026 @@ +/*- + * 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> + +#include <dev/ieee488/upd7210.h> +#include <dev/ieee488/ugpib.h> + +MALLOC_DEFINE(M_GPIB, "GPIB", "GPIB"); + +/* upd7210 generic stuff */ + +static void +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"); +} + +static u_int +read_reg(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); +} + +static void +write_reg(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 = read_reg(u, ISR1); + isr2 = read_reg(u, ISR2); + if (u->busy == 0 || u->irq == NULL || !u->irq(u)) { + printf("upd7210intr [%02x %02x %02x", + read_reg(u, DIR), isr1, isr2); + printf(" %02x %02x %02x %02x %02x] ", + read_reg(u, SPSR), + read_reg(u, ADSR), + read_reg(u, CPTR), + read_reg(u, ADR0), + read_reg(u, ADR1)); + print_isr(isr1, isr2); + printf("\n"); + write_reg(u, IMR1, 0); + write_reg(u, IMR2, 0); + } + mtx_unlock(&u->mutex); +} + +static int +upd7210_take_ctrl_async(struct upd7210 *u) +{ + int i; + + write_reg(u, AUXMR, AUXMR_TCA); + + if (!(read_reg(u, ADSR) & ADSR_ATN)) + return (0); + for (i = 0; i < 20; i++) { + DELAY(1); + if (!(read_reg(u, ADSR) & ADSR_ATN)) + return (0); + } + return (1); +} + +static int +upd7210_goto_standby(struct upd7210 *u) +{ + int i; + + write_reg(u, AUXMR, AUXMR_GTS); + + if (read_reg(u, ADSR) & ADSR_ATN) + return (0); + for (i = 0; i < 20; i++) { + DELAY(1); + if (read_reg(u, ADSR) & ADSR_ATN) + return (0); + } + return (1); +} + +static int +deadyet(struct upd7210 *u) +{ + struct timeval tv; + + if (!timevalisset(&u->deadline)) + return (0); + + getmicrouptime(&tv); + if (timevalcmp(&u->deadline, &tv, <)) { +printf("DEADNOW\n"); + return (1); + } + + return (0); +} + +/* Unaddressed Listen Only mode */ + +static int +gpib_l_irq(struct upd7210 *u) +{ + int i; + + if (u->rreg[ISR1] & 1) { + i = read_reg(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) + write_reg(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) + 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; + + write_reg(u, AUXMR, AUXMR_CRST); + DELAY(10000); + write_reg(u, AUXMR, C_ICR | 8); + DELAY(1000); + write_reg(u, ADR, 0x60); + write_reg(u, ADR, 0xe0); + write_reg(u, ADMR, 0x70); + write_reg(u, AUXMR, AUXMR_PON); + write_reg(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; + write_reg(u, AUXMR, AUXMR_CRST); + DELAY(10000); + write_reg(u, IMR1, 0x00); + write_reg(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) + write_reg(u, IMR1, 0x01); + mtx_unlock(&u->mutex); + return (error); +} + +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, +}; + +/* ibfoo API */ + +#include <dev/ieee488/ibfoo_int.h> + +struct handle { + LIST_ENTRY(handle) list; + int handle; + int pad; + int sad; + struct timeval timeout; + int eot; + int eos; +}; + +struct ibfoo { + struct upd7210 *upd7210; + LIST_HEAD(,handle) handles; + struct unrhdr *unrhdr; + + u_char *cmdbuf; + u_int cmdlen; + + struct handle *rdh; /* addressed for read */ + struct handle *wrh; /* addressed for write */ + + u_char *dobuf; + u_int dolen; + int doeoi; +}; + +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 u_int max_timeouts = sizeof timeouts / sizeof timeouts[0]; + +typedef int ibhandler_t(struct upd7210 *u, struct ibfoo_iocarg *ap); + +static int +gpib_ib_irq(struct upd7210 *u) +{ + struct ibfoo *ib; + + ib = u->ibfoo; + + if ((u->rreg[ISR2] & IXR2_CO) && ib->cmdlen > 0) { + write_reg(u, CDOR, *ib->cmdbuf); + ib->cmdbuf++; + ib->cmdlen--; + if (ib->cmdlen == 0) { + wakeup(ib); + write_reg(u, IMR2, 0); + } + return (1); + } + if ((u->rreg[ISR1] & IXR1_DO) && ib->dolen > 0) { + if (ib->dolen == 1 && ib->doeoi) + write_reg(u, AUXMR, AUXMR_SEOI); + write_reg(u, CDOR, *ib->dobuf); + ib->dobuf++; + ib->dolen--; + if (ib->dolen == 0) { + wakeup(ib); + write_reg(u, IMR1, 0); + } + return (1); + } + if (u->rreg[ISR1] & IXR1_ENDRX) { + write_reg(u, IMR1, 0); + wakeup(ib); + return (1); + } + + return (0); +} + +static void +config_eos(struct upd7210 *u, struct handle *h) +{ + int i; + + i = 0; + if (h->eos & 0x0400) { + write_reg(u, EOSR, h->eos & 0xff); + i |= AUXA_REOS; + } + if (h->eos & 0x1000) + i |= AUXA_BIN; + write_reg(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 ibfoo_iocarg *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; + if (timevalisset(&h->timeout)) { + getmicrouptime(&u->deadline); + timevaladd(&u->deadline, &h->timeout); + } else { + timevalclear(&u->deadline); + } + return (0); + } + } + ap->__iberr = EARG; + return (1); +} + +static int +do_cmd(struct upd7210 *u, u_char *cmd, int len) +{ + int i, i1, i2; + 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->cmdbuf = cmd; + ib->cmdlen = len; + + if (!(u->rreg[ISR2] & IXR2_CO)) { + i1 = read_reg(u, ISR1); + i2 = read_reg(u, ISR2); +#ifdef GPIB_DEBUG + print_isr(i1, i2); + printf("\n"); +#endif + } + write_reg(u, IMR2, IXR2_CO); + if (u->rreg[ISR2] & IXR2_CO) { + write_reg(u, CDOR, *ib->cmdbuf); + ib->cmdbuf++; + ib->cmdlen--; + } + + while (1) { + i = msleep(ib, &u->mutex, PZERO | PCATCH, "gpib_cmd", hz/10); + if (i == EINTR) + break; + if (u->rreg[ISR1] & IXR1_ERR) + break; + if (!ib->cmdlen) + break; + if (deadyet(u)) + break; + } + write_reg(u, IMR2, 0); + mtx_unlock(&u->mutex); + return (0); +} + +static int +do_odata(struct upd7210 *u, u_char *data, int len, int eos) +{ + int i1, i2, i; + struct ibfoo *ib; + + ib = u->ibfoo; + + mtx_lock(&u->mutex); + ib->dobuf = data; + ib->dolen = len; + ib->doeoi = 1; + + if (!(u->rreg[ISR1] & IXR1_DO)) { + i1 = read_reg(u, ISR1); + i2 = read_reg(u, ISR2); +#ifdef GPIB_DEBUG + print_isr(i1, i2); + printf("\n"); +#endif + } + write_reg(u, IMR1, IXR1_DO); + if (u->rreg[ISR1] & IXR1_DO) { + write_reg(u, CDOR, *ib->dobuf); + ib->dobuf++; + ib->dolen--; + } + while (1) { + i = msleep(ib, &u->mutex, PZERO | PCATCH, "gpib_out", hz/100); + if (i == EINTR) + break; + if (u->rreg[ISR1] & IXR1_ERR) + break; + if (!ib->dolen) + break; + if (deadyet(u)) + break; + } + write_reg(u, IMR2, 0); + mtx_unlock(&u->mutex); + return (len - ib->dolen); +} + +static int +do_idata(struct upd7210 *u, u_char *data, int len, int eos) +{ + int i1, i2, i, j; + + write_reg(u, IMR1, IXR1_ENDRX); + mtx_lock(&Giant); + isa_dmastart(ISADMA_READ, data, len, u->dmachan); + mtx_unlock(&Giant); + mtx_lock(&u->mutex); + write_reg(u, IMR2, IMR2_DMAI); + while (1) { + i = msleep(u->ibfoo, &u->mutex, PZERO | PCATCH, + "gpib_idata", hz/100); + if (i == EINTR) + break; + if (isa_dmatc(u->dmachan)) + break; + if (i == EWOULDBLOCK) { + i1 = read_reg(u, ISR1); + i2 = read_reg(u, ISR2); + } else { + i1 = u->rreg[ISR1]; + i2 = u->rreg[ISR2]; + } + if (i1 & IXR1_ENDRX) + break; + if (deadyet(u)) + break; + } + write_reg(u, IMR1, 0); + write_reg(u, IMR2, 0); + mtx_unlock(&u->mutex); + mtx_lock(&Giant); + j = isa_dmastatus(u->dmachan); + isa_dmadone(ISADMA_READ, data, len, u->dmachan); + mtx_unlock(&Giant); + if (deadyet(u)) { + return (-1); + } else { + return (len - j); + } +} + +#define ibask NULL +#define ibbna NULL +#define ibcac NULL +#define ibclr NULL +#define ibcmd NULL +#define ibcmda NULL +#define ibconfig NULL + +static int +ibdev(struct upd7210 *u, struct ibfoo_iocarg *ap) +{ + struct handle *h; + struct ibfoo *ib; + + if (ap->pad < 0 || + ap->pad > 30 || + (ap->sad != 0 && ap->sad < 0x60) || + ap->sad > 126) { + ap->__retval = -1; + ap->__iberr = EARG; + return (0); + } + + ib = u->ibfoo; + h = malloc(sizeof *h, M_GPIB, M_ZERO | M_WAITOK); + h->handle = alloc_unr(ib->unrhdr); + LIST_INSERT_HEAD(&ib->handles, h, list); + h->pad = ap->pad; + h->sad = ap->sad; + h->timeout = timeouts[ap->tmo]; + h->eot = ap->eot; + h->eos = ap->eos; + ap->__retval = h->handle; + return (0); +} + +#define ibdiag NULL +#define ibdma NULL + +static int +ibeos(struct upd7210 *u, struct ibfoo_iocarg *ap) +{ + struct handle *h; + struct ibfoo *ib; + + if (gethandle(u, ap, &h)) + return (0); + ib = u->ibfoo; + h->eos = ap->eos; + if (ib->rdh == h) + config_eos(u, h); + ap->__retval = 0; + return (0); +} + +#define ibeot NULL +#define ibevent NULL +#define ibfind NULL +#define ibgts NULL +#define ibist NULL +#define iblines NULL +#define ibllo NULL +#define ibln NULL +#define ibloc NULL +#define ibonl NULL +#define ibpad NULL +#define ibpct NULL +#define ibpoke NULL +#define ibppc NULL + +static int +ibrd(struct upd7210 *u, struct ibfoo_iocarg *ap) +{ + struct ibfoo *ib; + struct handle *h; + u_char buf[10], *bp; + int i, j, error, bl, bc; + u_char *dp; + + ib = u->ibfoo; + if (gethandle(u, ap, &h)) + return (0); + bl = ap->cnt; + if (bl > PAGE_SIZE) + bl = PAGE_SIZE; + bp = malloc(bl, M_GPIB, M_WAITOK); + + if (ib->rdh != h) { + i = 0; + buf[i++] = UNT; + buf[i++] = UNL; + buf[i++] = LAD | 0; + buf[i++] = TAD | h->pad; + if (h->sad) + buf[i++] = h->sad; + i = do_cmd(u, buf, i); + config_eos(u, h); + ib->rdh = h; + ib->wrh = NULL; + upd7210_goto_standby(u); + } + ap->__ibcnt = 0; + dp = ap->buffer; + bc = ap->cnt; + error = 0; + while (bc > 0) { + j = imin(bc, PAGE_SIZE); + i = do_idata(u, bp, j, 1); + if (i <= 0) + break; + error = copyout(bp, dp , i); + if (error) + break; + ap->__ibcnt += i; + if (i != j) + break; + bc -= i; + dp += i; + } + free(bp, M_GPIB); + ap->__retval = 0; + return (error); +} + +#define ibrda NULL +#define ibrdf NULL +#define ibrdkey NULL +#define ibrpp NULL +#define ibrsc NULL +#define ibrsp NULL +#define ibrsv NULL +#define ibsad NULL +#define ibsgnl NULL +#define ibsic NULL +#define ibsre NULL +#define ibsrq NULL +#define ibstop NULL + +static int +ibtmo(struct upd7210 *u, struct ibfoo_iocarg *ap) +{ + struct handle *h; + + if (gethandle(u, ap, &h)) + return (0); + h->timeout = timeouts[ap->tmo]; + return (0); +} + +#define ibtrap NULL +#define ibtrg NULL +#define ibwait NULL + +static int +ibwrt(struct upd7210 *u, struct ibfoo_iocarg *ap) +{ + struct ibfoo *ib; + struct handle *h; + u_char buf[10], *bp; + int i; + + ib = u->ibfoo; + if (gethandle(u, ap, &h)) + return (0); + bp = malloc(ap->cnt, M_GPIB, M_WAITOK); + i = copyin(ap->buffer, bp, ap->cnt); + if (i) { + free(bp, M_GPIB); + return (i); + } + if (ib->wrh != h) { + i = 0; + buf[i++] = UNT; + buf[i++] = UNL; + buf[i++] = LAD | h->pad; + if (h->sad) + buf[i++] = LAD | TAD | h->sad; + buf[i++] = TAD | 0; + i = do_cmd(u, buf, i); + ib->rdh = NULL; + ib->wrh = h; + upd7210_goto_standby(u); + } + i = do_odata(u, bp, ap->cnt, 1); + ap->__ibcnt = i; + ap->__retval = 0; + free(bp, M_GPIB); + return (0); +} + +#define ibwrta NULL +#define ibwrtf NULL +#define ibwrtkey NULL +#define ibxtrc NULL + +static struct ibhandler { + const char *name; + ibhandler_t *func; + u_int args; +} ibhandlers[] = { + [__ID_IBASK] = { "ibask", ibask, __F_HANDLE | __F_OPTION | __F_RETVAL }, + [__ID_IBBNA] = { "ibbna", ibbna, __F_HANDLE | __F_BDNAME }, + [__ID_IBCAC] = { "ibcac", ibcac, __F_HANDLE | __F_V }, + [__ID_IBCLR] = { "ibclr", ibclr, __F_HANDLE }, + [__ID_IBCMDA] = { "ibcmda", ibcmda, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBCMD] = { "ibcmd", ibcmd, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBCONFIG] = { "ibconfig", ibconfig, __F_HANDLE | __F_OPTION | __F_VALUE }, + [__ID_IBDEV] = { "ibdev", ibdev, __F_BOARDID | __F_PAD | __F_SAD | __F_TMO | __F_EOT | __F_EOS }, + [__ID_IBDIAG] = { "ibdiag", ibdiag, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBDMA] = { "ibdma", ibdma, __F_HANDLE | __F_V }, + [__ID_IBEOS] = { "ibeos", ibeos, __F_HANDLE | __F_EOS }, + [__ID_IBEOT] = { "ibeot", ibeot, __F_HANDLE | __F_V }, + [__ID_IBEVENT] = { "ibevent", ibevent, __F_HANDLE | __F_EVENT }, + [__ID_IBFIND] = { "ibfind", ibfind, __F_BDNAME }, + [__ID_IBGTS] = { "ibgts", ibgts, __F_HANDLE | __F_V }, + [__ID_IBIST] = { "ibist", ibist, __F_HANDLE | __F_V }, + [__ID_IBLINES] = { "iblines", iblines, __F_HANDLE | __F_LINES }, + [__ID_IBLLO] = { "ibllo", ibllo, __F_HANDLE }, + [__ID_IBLN] = { "ibln", ibln, __F_HANDLE | __F_PADVAL | __F_SADVAL | __F_LISTENFLAG }, + [__ID_IBLOC] = { "ibloc", ibloc, __F_HANDLE }, + [__ID_IBONL] = { "ibonl", ibonl, __F_HANDLE | __F_V }, + [__ID_IBPAD] = { "ibpad", ibpad, __F_HANDLE | __F_V }, + [__ID_IBPCT] = { "ibpct", ibpct, __F_HANDLE }, + [__ID_IBPOKE] = { "ibpoke", ibpoke, __F_HANDLE | __F_OPTION | __F_VALUE }, + [__ID_IBPPC] = { "ibppc", ibppc, __F_HANDLE | __F_V }, + [__ID_IBRDA] = { "ibrda", ibrda, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBRDF] = { "ibrdf", ibrdf, __F_HANDLE | __F_FLNAME }, + [__ID_IBRDKEY] = { "ibrdkey", ibrdkey, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBRD] = { "ibrd", ibrd, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBRPP] = { "ibrpp", ibrpp, __F_HANDLE | __F_PPR }, + [__ID_IBRSC] = { "ibrsc", ibrsc, __F_HANDLE | __F_V }, + [__ID_IBRSP] = { "ibrsp", ibrsp, __F_HANDLE | __F_SPR }, + [__ID_IBRSV] = { "ibrsv", ibrsv, __F_HANDLE | __F_V }, + [__ID_IBSAD] = { "ibsad", ibsad, __F_HANDLE | __F_V }, + [__ID_IBSGNL] = { "ibsgnl", ibsgnl, __F_HANDLE | __F_V }, + [__ID_IBSIC] = { "ibsic", ibsic, __F_HANDLE }, + [__ID_IBSRE] = { "ibsre", ibsre, __F_HANDLE | __F_V }, + [__ID_IBSRQ] = { "ibsrq", ibsrq, __F_FUNC }, + [__ID_IBSTOP] = { "ibstop", ibstop, __F_HANDLE }, + [__ID_IBTMO] = { "ibtmo", ibtmo, __F_HANDLE | __F_TMO }, + [__ID_IBTRAP] = { "ibtrap", ibtrap, __F_MASK | __F_MODE }, + [__ID_IBTRG] = { "ibtrg", ibtrg, __F_HANDLE }, + [__ID_IBWAIT] = { "ibwait", ibwait, __F_HANDLE | __F_MASK }, + [__ID_IBWRTA] = { "ibwrta", ibwrta, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBWRTF] = { "ibwrtf", ibwrtf, __F_HANDLE | __F_FLNAME }, + [__ID_IBWRTKEY] = { "ibwrtkey", ibwrtkey, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBWRT] = { "ibwrt", ibwrt, __F_HANDLE | __F_BUFFER | __F_CNT }, + [__ID_IBXTRC] = { "ibxtrc", ibxtrc, __F_HANDLE | __F_BUFFER | __F_CNT }, +}; + +static u_int max_ibhandler = sizeof ibhandlers / sizeof ibhandlers[0]; + +static int +gpib_ib_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct upd7210 *u; + struct ibfoo *ib; + int error; + + u = dev->si_drv1; + + mtx_lock(&u->mutex); + if (u->busy) { + mtx_unlock(&u->mutex); + return (EBUSY); + } + u->busy = 1; + mtx_unlock(&u->mutex); + + 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_GPIB, M_WAITOK | M_ZERO); + LIST_INIT(&ib->handles); + ib->unrhdr = new_unrhdr(0, INT_MAX); + dev->si_drv2 = ib; + ib->upd7210 = u; + u->ibfoo = ib; + u->irq = gpib_ib_irq; + + write_reg(u, AUXMR, AUXMR_CRST); + DELAY(10000); + DELAY(1000); + write_reg(u, IMR1, 0x00); + write_reg(u, IMR2, 0x00); + write_reg(u, SPMR, 0x00); + write_reg(u, ADR, 0x00); + write_reg(u, ADR, ADR_ARS | ADR_DL | ADR_DT); + write_reg(u, ADMR, ADMR_ADM0 | ADMR_TRM0 | ADMR_TRM1); + write_reg(u, EOSR, 0x00); + write_reg(u, AUXMR, C_ICR | 8); + write_reg(u, AUXMR, C_PPR | PPR_U); + write_reg(u, AUXMR, C_AUXA); + write_reg(u, AUXMR, C_AUXB + 3); + write_reg(u, AUXMR, C_AUXE + 0); + write_reg(u, AUXMR, AUXMR_PON); + write_reg(u, AUXMR, AUXMR_CIFC); + DELAY(100); + write_reg(u, AUXMR, AUXMR_SIFC); + write_reg(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_GPIB); + + mtx_lock(&Giant); + isa_dma_release(u->dmachan); + mtx_unlock(&Giant); + mtx_lock(&u->mutex); + u->busy = 0; + write_reg(u, IMR1, 0x00); + write_reg(u, IMR2, 0x00); + write_reg(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 ibfoo_iocarg *ap; + struct ibhandler *ih; + struct upd7210 *u; + int error; + + u = dev->si_drv1; + + if (cmd != GPIB_IBFOO) + return (ENOIOCTL); + + 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 (ap->__field & __F_TMO) { + if (ap->tmo < 0 || ap->tmo >= max_timeouts) { + ap->__retval = -1; + ap->__iberr = EARG; + return (0); + } + } + + if (ap->__field & __F_EOS) { + if (ap->eos & ~(REOS | XEOS | BIN | 0xff)) { + ap->__retval = -1; + ap->__iberr = EARG; + return (0); + } + if (ap->eos & (REOS | XEOS)) { + if ((ap->eos & (BIN | 0x80)) == 0x80) { + ap->__retval = -1; + ap->__iberr = EARG; + return (0); + } + } else if (ap->eos != 0) { + ap->__retval = -1; + ap->__iberr = EARG; + return (0); + } + } + + mtx_lock(&u->mutex); + while(u->busy != 1) { + error = msleep(u->ibfoo, &u->mutex, PZERO | PCATCH, + "gpib_ibioctl", 0); + if (error) { + mtx_unlock(&u->mutex); + return (EINTR); + } + } + u->busy = 2; + mtx_unlock(&u->mutex); + +#ifdef GPIB_DEBUG + 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=%d", 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=%d", ap->pad); + if (ap->__field & __F_SAD) printf(" sad=%d", ap->sad); + if (ap->__field & __F_BUFFER) printf(" buffer=%p", ap->buffer); + if (ap->__field & __F_CNT) printf(" cnt=%ld", ap->cnt); + /* XXX more ... */ + printf(")\n"); +#endif + + ap->__iberr = 0; + error = EOPNOTSUPP; + if (ih->func != NULL) + error = ih->func(u, ap); + if (error) { + ap->__retval = EDVR; + ap->__iberr = EDVR; + ap->__ibcnt = error; + } else if (ap->__iberr) { + ap->__retval = -1; + } +#ifdef GPIB_DEBUG + printf("%s(...) = %d (error=%d)\n", ih->name, ap->__retval, error); +#endif + mtx_lock(&u->mutex); + u->busy = 1; + wakeup(u->ibfoo); + mtx_unlock(&u->mutex); + return (error); +} + +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, +}; + +/* 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 0000000..623fb11 --- /dev/null +++ b/sys/dev/ieee488/upd7210.h @@ -0,0 +1,213 @@ +/*- + * 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. + */ + +/* upd7210 interface definitions */ + +struct upd7210; + +struct ibfoo; + +void upd7210intr(void *); +void upd7210attach(struct upd7210 *); + +typedef int upd7210_irq_t(struct upd7210 *); + +struct upd7210 { + bus_space_handle_t reg_handle[8]; + bus_space_tag_t reg_tag[8]; + u_int reg_offset[8]; + u_int dmachan; + + /* private stuff */ + struct timeval deadline; + 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; +}; + +/* 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 */ + |