mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-12-02 19:22:47 +00:00
1128 lines
26 KiB
C
1128 lines
26 KiB
C
/*-
|
|
* Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org>
|
|
* Copyright (c) 2010 Joerg Wunsch <joerg@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/rman.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>
|
|
#include <dev/ieee488/tnt4882.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,
|
|
FIFO_IDATA,
|
|
FIFO_ODATA,
|
|
FIFO_CMD
|
|
} 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 isr_3)
|
|
{
|
|
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;
|
|
case FIFO_IDATA:
|
|
if (!(isr_3 & 0x15))
|
|
return (0);
|
|
while (ib->buflen != 0 && (isr_3 & 0x04 /* NEF */) != 0) {
|
|
*ib->buf = bus_read_1(u->reg_res[0], fifob);
|
|
ib->buf++;
|
|
ib->buflen--;
|
|
isr_3 = bus_read_1(u->reg_res[0], isr3);
|
|
}
|
|
if ((isr_3 & 0x01) != 0 /* xfr done */ ||
|
|
(u->rreg[ISR1] & IXR1_ENDRX) != 0 ||
|
|
ib->buflen == 0)
|
|
break;
|
|
if (isr_3 & 0x10)
|
|
/* xfr stopped */
|
|
bus_write_1(u->reg_res[0], cmdr, 0x04); /* GO */
|
|
upd7210_wr(u, AUXMR, AUXMR_RFD);
|
|
return (1);
|
|
case FIFO_CMD:
|
|
case FIFO_ODATA:
|
|
if (!(isr_3 & 0x19))
|
|
return (0);
|
|
if (ib->buflen == 0)
|
|
/* xfr DONE */
|
|
break;
|
|
while (ib->buflen != 0 && (isr_3 & 0x08 /* NFF */) != 0) {
|
|
bus_write_1(u->reg_res[0], fifob, *ib->buf);
|
|
ib->buf++;
|
|
ib->buflen--;
|
|
isr_3 = bus_read_1(u->reg_res[0], isr3);
|
|
}
|
|
if (isr_3 & 0x10)
|
|
/* xfr stopped */
|
|
bus_write_1(u->reg_res[0], cmdr, 0x04); /* GO */
|
|
if (ib->buflen == 0)
|
|
/* no more NFF interrupts wanted */
|
|
bus_write_1(u->reg_res[0], imr3, 0x11); /* STOP IE, DONE IE */
|
|
return (1);
|
|
default:
|
|
return (0);
|
|
}
|
|
upd7210_wr(u, IMR1, 0);
|
|
upd7210_wr(u, IMR2, 0);
|
|
if (u->use_fifo) {
|
|
bus_write_1(u->reg_res[0], imr3, 0x00);
|
|
bus_write_1(u->reg_res[0], cmdr, 0x22); /* soft RESET */
|
|
}
|
|
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_int isr_3;
|
|
|
|
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);
|
|
if (u->use_fifo)
|
|
isr_3 = bus_read_1(u->reg_res[0], isr3);
|
|
else
|
|
isr_3 = 0;
|
|
gpib_ib_irq(u, isr_3);
|
|
}
|
|
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);
|
|
if (u->use_fifo) {
|
|
bus_write_1(u->reg_res[0], imr3, 0x00);
|
|
bus_write_1(u->reg_res[0], cmdr, 0x22); /* soft RESET */
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
if ((u->rreg[ISR1] & IXR1_ENDRX) != 0) {
|
|
ib->ap->__retval |= END;
|
|
ib->ap->__ibsta |= END;
|
|
}
|
|
if ((u->rreg[ISR2] & IXR2_SRQI) != 0) {
|
|
ib->ap->__retval |= SRQI;
|
|
ib->ap->__ibsta |= SRQI;
|
|
}
|
|
ib->mode = BUSY;
|
|
ib->buf = NULL;
|
|
upd7210_wr(u, IMR1, 0);
|
|
upd7210_wr(u, IMR2, 0);
|
|
if (u->use_fifo)
|
|
bus_write_1(u->reg_res[0], imr3, 0x00);
|
|
}
|
|
|
|
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->buf = cmd;
|
|
ib->buflen = len;
|
|
if (u->use_fifo) {
|
|
/* TNT5004 or TNT4882 in FIFO mode */
|
|
ib->mode = FIFO_CMD;
|
|
upd7210_wr(u, AUXMR, 0x51); /* holdoff immediately */
|
|
bus_write_1(u->reg_res[0], cmdr, 0x10); /* reset FIFO */
|
|
bus_write_1(u->reg_res[0], cfg, 0x80); /* CMD, xfer OUT, 8-bit FIFO */
|
|
bus_write_1(u->reg_res[0], imr3, 0x19); /* STOP IE, NFF IE, DONE IE */
|
|
bus_write_1(u->reg_res[0], cnt0, -len);
|
|
bus_write_1(u->reg_res[0], cnt1, (-len) >> 8);
|
|
bus_write_1(u->reg_res[0], cnt2, (-len) >> 16);
|
|
bus_write_1(u->reg_res[0], cnt3, (-len) >> 24);
|
|
bus_write_1(u->reg_res[0], cmdr, 0x04); /* GO */
|
|
} else {
|
|
ib->mode = PIO_CMD;
|
|
upd7210_wr(u, IMR2, IXR2_CO);
|
|
gpib_ib_irq(u, 0);
|
|
}
|
|
|
|
gpib_ib_wait_xfer(u, ib);
|
|
|
|
if (u->use_fifo)
|
|
bus_write_1(u->reg_res[0], cmdr, 0x08); /* STOP */
|
|
|
|
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->buf = data;
|
|
ib->buflen = len;
|
|
if (u->use_fifo) {
|
|
/* TNT5004 or TNT4882 in FIFO mode */
|
|
ib->mode = FIFO_ODATA;
|
|
bus_write_1(u->reg_res[0], cmdr, 0x10); /* reset FIFO */
|
|
if (ib->doeoi)
|
|
bus_write_1(u->reg_res[0], cfg, 0x08); /* CCEN */
|
|
else
|
|
bus_write_1(u->reg_res[0], cfg, 0x00); /* xfer OUT, 8-bit FIFO */
|
|
bus_write_1(u->reg_res[0], imr3, 0x19); /* STOP IE, NFF IE, DONE IE */
|
|
bus_write_1(u->reg_res[0], cnt0, -len);
|
|
bus_write_1(u->reg_res[0], cnt1, (-len) >> 8);
|
|
bus_write_1(u->reg_res[0], cnt2, (-len) >> 16);
|
|
bus_write_1(u->reg_res[0], cnt3, (-len) >> 24);
|
|
bus_write_1(u->reg_res[0], cmdr, 0x04); /* GO */
|
|
} else {
|
|
ib->mode = PIO_ODATA;
|
|
upd7210_wr(u, IMR1, IXR1_DO);
|
|
}
|
|
|
|
gpib_ib_wait_xfer(u, ib);
|
|
|
|
if (u->use_fifo)
|
|
bus_write_1(u->reg_res[0], cmdr, 0x08); /* STOP */
|
|
|
|
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->buf = data;
|
|
ib->buflen = len;
|
|
if (u->use_fifo) {
|
|
/* TNT5004 or TNT4882 in FIFO mode */
|
|
ib->mode = FIFO_IDATA;
|
|
bus_write_1(u->reg_res[0], cmdr, 0x10); /* reset FIFO */
|
|
bus_write_1(u->reg_res[0], cfg, 0x20); /* xfer IN, 8-bit FIFO */
|
|
bus_write_1(u->reg_res[0], cnt0, -len);
|
|
bus_write_1(u->reg_res[0], cnt1, (-len) >> 8);
|
|
bus_write_1(u->reg_res[0], cnt2, (-len) >> 16);
|
|
bus_write_1(u->reg_res[0], cnt3, (-len) >> 24);
|
|
bus_write_1(u->reg_res[0], cmdr, 0x04); /* GO */
|
|
upd7210_wr(u, AUXMR, AUXMR_RFD);
|
|
bus_write_1(u->reg_res[0], imr3, 0x15); /* STOP IE, NEF IE, DONE IE */
|
|
} else {
|
|
ib->mode = PIO_IDATA;
|
|
upd7210_wr(u, IMR1, IXR1_DI);
|
|
}
|
|
|
|
gpib_ib_wait_xfer(u, ib);
|
|
|
|
if (u->use_fifo)
|
|
bus_write_1(u->reg_res[0], cmdr, 0x08); /* STOP */
|
|
|
|
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;
|
|
isa_dmastart(ISADMA_READ, data, len, u->dmachan);
|
|
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);
|
|
j = isa_dmastatus(u->dmachan);
|
|
isa_dmadone(ISADMA_READ, data, len, u->dmachan);
|
|
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) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
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, CALLOUT_MPSAFE);
|
|
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);
|
|
if (u->use_fifo) {
|
|
bus_write_1(u->reg_res[0], imr3, 0x00);
|
|
bus_write_1(u->reg_res[0], cmdr, 0x22); /* soft reset */
|
|
bus_write_1(u->reg_res[0], cmdr, 0x03); /* set system
|
|
* controller bit */
|
|
}
|
|
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) {
|
|
isa_dma_release(u->dmachan);
|
|
}
|
|
mtx_lock(&u->mutex);
|
|
u->busy = 0;
|
|
ibdebug = 0;
|
|
upd7210_wr(u, IMR1, 0x00);
|
|
upd7210_wr(u, IMR2, 0x00);
|
|
if (u->use_fifo) {
|
|
bus_write_1(u->reg_res[0], imr3, 0x00);
|
|
bus_write_1(u->reg_res[0], cmdr, 0x02); /* clear system
|
|
* controller bit */
|
|
}
|
|
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,
|
|
};
|