summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/labpc.c
diff options
context:
space:
mode:
authordufault <dufault@FreeBSD.org>1995-04-28 00:51:40 +0000
committerdufault <dufault@FreeBSD.org>1995-04-28 00:51:40 +0000
commit6702bdcc1ba2ea51bb08f3c1785a6f5da76b017d (patch)
treeb04da640a66a433a452ccf805eb30581306a748f /sys/i386/isa/labpc.c
parenta7372e7b99b33efab9bd9df0d18d63bdb4f486a5 (diff)
downloadFreeBSD-src-6702bdcc1ba2ea51bb08f3c1785a6f5da76b017d.zip
FreeBSD-src-6702bdcc1ba2ea51bb08f3c1785a6f5da76b017d.tar.gz
Add National Instruments "LabPC" driver
Diffstat (limited to 'sys/i386/isa/labpc.c')
-rw-r--r--sys/i386/isa/labpc.c1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/sys/i386/isa/labpc.c b/sys/i386/isa/labpc.c
new file mode 100644
index 0000000..e5ddeac
--- /dev/null
+++ b/sys/i386/isa/labpc.c
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (c) 1995 HD Associates, Inc.
+ * All rights reserved.
+ *
+ * HD Associates, Inc.
+ * PO Box 276
+ * Pepperell, MA 01463-0276
+ * dufault@hda.com
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by HD Associates, Inc.
+ * 4. The name of HD Associates, Inc.
+ * may not be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``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 REGENTS 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.
+ *
+ * Written by:
+ * Peter Dufault
+ * dufault@hda.com
+ */
+
+#include "labpc.h"
+#include "aio.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <sys/systm.h>
+#include <sys/devconf.h>
+
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/buf.h>
+#include <machine/devconf.h>
+#include <machine/clock.h>
+
+
+#ifndef LABPC_DEFAULT_HERZ
+#define LABPC_DEFAULT_HERZ 500
+#endif
+
+/* Minor number:
+ * UUSIDCCC
+ * UU: Board unit.
+ * S: SCAN bit for scan enable.
+ * I: INTERVAL for interval support
+ * D: 1: Digital I/O, 0: Analog I/O
+ * CCC: Channel.
+ * Analog (D==0):
+ * input: channel must be 0 to 7.
+ * output: channel must be 0 to 2
+ * 0: D-A 0
+ * 1: D-A 1
+ * 2: Alternate channel 0 then 1
+ *
+ * Digital (D==1):
+ * input: Channel must be 0 to 2.
+ * output: Channel must be 0 to 2.
+ */
+
+/* Up to four boards:
+ */
+#define MAX_UNITS 4
+#define UNIT(dev) (((minor(dev) & 0xB0) >> 6) & 0x3)
+
+#define SCAN(dev) ((minor(dev) & 0x20) >> 5)
+#define INTERVAL(dev) ((minor(dev) & 0x10) >> 4)
+#define DIGITAL(dev) ((minor(dev) & 0x08) >> 3)
+
+/* Eight channels:
+ */
+
+#define CHAN(dev) (minor(dev) & 0x7)
+
+/* History: Derived from "dt2811.c" March 1995
+ */
+
+struct ctlr
+{
+ int err;
+#define DROPPED_INPUT 0x100
+ int base;
+ int unit;
+ unsigned long flags;
+#define BUSY 0x00000001
+
+ u_char cr_image[4];
+
+ u_short sample_us;
+
+ struct buf start_queue; /* Start queue */
+ struct buf *last; /* End of start queue */
+ u_char *data;
+ u_char *data_end;
+ long tmo; /* Timeout in Herz */
+
+ int gains[8];
+
+ dev_t dev; /* Copy of device */
+
+ void (*starter)(struct ctlr *ctlr, long count);
+ void (*stop)(struct ctlr *ctlr);
+ void (*intr)(struct ctlr *ctlr);
+
+ /* Digital I/O support. Copy of Data Control Register for 8255:
+ */
+ u_char dcr_val, dcr_is;
+
+ /* Device configuration structure:
+ */
+ struct kern_devconf kdc;
+};
+
+#ifdef LOUTB
+/* loutb is a slow outb for debugging. The overrun test may fail
+ * with this for some slower processors.
+ */
+static inline void loutb(int port, u_char val)
+{
+ outb(port, val);
+ DELAY(1);
+}
+#else
+#define loutb(port, val) outb(port, val)
+#endif
+
+struct ctlr **labpcs; /* XXX: Should be dynamic */
+
+/* CR_EXPR: A macro that sets the shadow register in addition to
+ * sending out the data.
+ */
+#define CR_EXPR(LABPC, CR, EXPR) do { \
+ (LABPC)->cr_image[CR - 1] EXPR ; \
+ loutb((LABPC)->base + (CR == 4 ? 0x0F : CR - 1), (LABPC)->cr_image[(CR - 1)]); \
+} while (0)
+
+#define CR_CLR(LABPC, CR) CR_EXPR(LABPC, CR, &=0)
+#define CR_REFRESH(LABPC, CR) CR_EXPR(LABPC, CR, &=0xff)
+#define CR_SET(LABPC, CR, EXPR) CR_EXPR(LABPC, CR, = EXPR)
+
+/* Configuration and Status Register Group.
+ */
+#define CR1(LABPC) ((LABPC)->base + 0x00) /* Page 4-5 */
+ #define SCANEN 0x80
+ #define GAINMASK 0x70
+ #define GAIN(LABPC, SEL) do { \
+ (LABPC)->cr_image[1 - 1] &= ~GAINMASK; \
+ (LABPC)->cr_image[1 - 1] |= (SEL << 4); \
+ loutb((LABPC)->base + (1 - 1), (LABPC)->cr_image[(1 - 1)]); \
+ } while (0)
+
+ #define TWOSCMP 0x08
+ #define MAMASK 0x07
+ #define MA(LABPC, SEL) do { \
+ (LABPC)->cr_image[1 - 1] &= ~MAMASK; \
+ (LABPC)->cr_image[1 - 1] |= SEL; \
+ loutb((LABPC)->base + (1 - 1), (LABPC)->cr_image[(1 - 1)]); \
+ } while (0)
+
+#define STATUS(LABPC) ((LABPC)->base + 0x00) /* Page 4-7 */
+ #define LABPCPLUS 0x80
+ #define EXTGATA0 0x40
+ #define GATA0 0x20
+ #define DMATC 0x10
+ #define CNTINT 0x08
+ #define OVERFLOW 0x04
+ #define OVERRUN 0x02
+ #define DAVAIL 0x01
+
+#define CR2(LABPC) ((LABPC)->base + 0x01) /* Page 4-9 */
+ #define LDAC1 0x80
+ #define LDAC0 0x40
+ #define _2SDAC1 0x20
+ #define _2SDAC0 0x10
+ #define TBSEL 0x08
+ #define SWTRIG 0x04
+ #define HWTRIG 0x02
+ #define PRETRIG 0x01
+ #define SWTRIGGERRED(LABPC) ((LABPC->cr_image[1]) & SWTRIG)
+
+#define CR3(LABPC) ((LABPC)->base + 0x02) /* Page 4-11 */
+ #define FIFOINTEN 0x20
+ #define ERRINTEN 0x10
+ #define CNTINTEN 0x08
+ #define TCINTEN 0x04
+ #define DIOINTEN 0x02
+ #define DMAEN 0x01
+
+ #define ALLINTEN 0x3E
+ #define FIFOINTENABLED(LABPC) ((LABPC->cr_image[2]) & FIFOINTEN)
+
+#define CR4(LABPC) ((LABPC)->base + 0x0F) /* Page 4-13 */
+ #define ECLKRCV 0x10
+ #define SE_D 0x08
+ #define ECKDRV 0x04
+ #define EOIRCV 0x02
+ #define INTSCAN 0x01
+
+/* Analog Input Register Group
+ */
+#define ADFIFO(LABPC) ((LABPC)->base + 0x0A) /* Page 4-16 */
+#define ADCLEAR(LABPC) ((LABPC)->base + 0x08) /* Page 4-18 */
+#define ADSTART(LABPC) ((LABPC)->base + 0x03) /* Page 4-19 */
+#define DMATCICLR(LABPC) ((LABPC)->base + 0x0A) /* Page 4-20 */
+
+/* Analog Output Register Group
+ */
+#define DAC0L(LABPC) ((LABPC)->base + 0x04) /* Page 4-22 */
+#define DAC0H(LABPC) ((LABPC)->base + 0x05) /* Page 4-22 */
+#define DAC1L(LABPC) ((LABPC)->base + 0x06) /* Page 4-22 */
+#define DAC1H(LABPC) ((LABPC)->base + 0x07) /* Page 4-22 */
+
+/* 8253 registers:
+ */
+#define A0DATA(LABPC) ((LABPC)->base + 0x14)
+#define A1DATA(LABPC) ((LABPC)->base + 0x15)
+#define A2DATA(LABPC) ((LABPC)->base + 0x16)
+#define AMODE(LABPC) ((LABPC)->base + 0x17)
+
+#define TICR(LABPC) ((LABPC)->base + 0x0c)
+
+#define B0DATA(LABPC) ((LABPC)->base + 0x18)
+#define B1DATA(LABPC) ((LABPC)->base + 0x19)
+#define B2DATA(LABPC) ((LABPC)->base + 0x1A)
+#define BMODE(LABPC) ((LABPC)->base + 0x1B)
+
+/* 8255 registers:
+ */
+
+#define PORTX(LABPC, X) ((LABPC)->base + 0x10 + X)
+
+#define PORTA(LABPC) PORTX(LABPC, 0)
+#define PORTB(LABPC) PORTX(LABPC, 1)
+#define PORTC(LABPC) PORTX(LABPC, 2)
+
+#define DCR(LABPC) ((LABPC)->base + 0x13)
+
+extern int labpcattach(struct isa_device *dev);
+extern int labpcdetach(struct isa_device *dev);
+extern int labpcprobe(struct isa_device *dev);
+struct isa_driver labpcdriver =
+ { labpcprobe, labpcattach, "labpc", 0 /* , labpcdetach */ };
+
+static void
+bp_done(struct buf *bp, int err)
+{
+ if (err)
+ {
+ bp->b_error = err;
+ bp->b_flags |= B_ERROR;
+ }
+
+ biodone(bp);
+}
+
+static void tmo_stop(void *p);
+
+static void
+done_and_dequeu(struct ctlr *ctlr, struct buf *bp, int err)
+{
+ static void start(struct ctlr *ctlr);
+
+ bp->b_resid = ctlr->data_end - ctlr->data;
+
+ ctlr->data = 0;
+
+ ctlr->start_queue.b_actf = bp->b_actf;
+ bp_done(bp, err);
+
+ untimeout(tmo_stop, ctlr);
+
+ start(ctlr);
+}
+
+static inline void
+ad_clear(struct ctlr *ctlr)
+{
+ int i;
+ loutb(ADCLEAR(ctlr), 0);
+ for (i = 0; i < 10000 && (inb(STATUS(ctlr)) & GATA0); i++)
+ ;
+ (void)inb(ADFIFO(ctlr));
+ (void)inb(ADFIFO(ctlr));
+}
+
+/* reset: Reset the board following the sequence on page 5-1
+ */
+static inline void
+reset(struct ctlr *ctlr)
+{
+ int s = splhigh();
+
+ CR_CLR(ctlr, 3); /* Turn off interrupts first */
+ splx(s);
+
+ CR_CLR(ctlr, 1);
+ CR_CLR(ctlr, 2);
+ CR_CLR(ctlr, 4);
+
+ loutb(AMODE(ctlr), 0x34);
+ loutb(A0DATA(ctlr),0x0A);
+ loutb(A0DATA(ctlr),0x00);
+
+ loutb(DMATCICLR(ctlr), 0x00);
+ loutb(TICR(ctlr), 0x00);
+
+ ad_clear(ctlr);
+
+ loutb(DAC0L(ctlr), 0);
+ loutb(DAC0H(ctlr), 0);
+ loutb(DAC1L(ctlr), 0);
+ loutb(DAC1H(ctlr), 0);
+
+ ad_clear(ctlr);
+}
+
+static int
+labpc_goaway(struct kern_devconf *kdc, int force)
+{
+ if(force) {
+ dev_detach(kdc);
+ return 0;
+ } else {
+ return EBUSY; /* XXX fix */
+ }
+}
+
+static struct kern_devconf kdc_template = {
+ 0, 0, 0, /* filled in by dev_attach */
+ "labpc", 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, labpc_goaway, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+ 0, /* parentdata */
+ DC_UNKNOWN,
+ "?" /* Description (filled in later ) */
+};
+
+static inline void
+labpc_registerdev(struct isa_device *id)
+{
+ struct kern_devconf *kdc = &labpcs[id->id_unit]->kdc;
+ kdc->kdc_unit = id->id_unit;
+ kdc->kdc_parentdata = id;
+ dev_attach(kdc);
+}
+
+/* overrun: slam the start convert register and OVERRUN should get set:
+ */
+static u_char
+overrun(struct ctlr *ctlr)
+{
+ int i;
+
+ u_char status = inb(STATUS(ctlr));
+ for (i = 0; ((status & OVERRUN) == 0) && i < 100; i++)
+ {
+ loutb(ADSTART(ctlr), 1);
+ status = inb(STATUS(ctlr));
+ }
+
+ return status;
+}
+
+static int
+labpcinit(void)
+{
+ if (NLABPC > MAX_UNITS)
+ return 0;
+
+ labpcs = malloc(NLABPC * sizeof(struct ctlr *), M_DEVBUF, M_NOWAIT);
+ if (labpcs)
+ {
+ bzero(labpcs, NLABPC * sizeof(struct cltr *));
+ return 1;
+ }
+ return 0;
+}
+
+int labpcprobe(struct isa_device *dev)
+{
+ static unit;
+ struct ctlr scratch, *ctlr;
+ u_char status;
+
+ if (!labpcs)
+ {
+ if (labpcinit() == 0)
+ {
+ printf("labpcprobe: init failed\n");
+ return 0;
+ }
+ }
+
+ if (unit > NLABPC)
+ {
+ printf("Too many LAB-PCs. Reconfigure O/S.\n");
+ return 0;
+ }
+ ctlr = &scratch; /* Need somebody with the right base for the macros */
+ ctlr->base = dev->id_iobase;
+
+ /* XXX: There really isn't a perfect way to probe this board.
+ * Here is my best attempt:
+ */
+ reset(ctlr);
+
+ /* After reset none of these bits should be set:
+ */
+ status = inb(STATUS(ctlr));
+ if (status & (GATA0 | OVERFLOW | DAVAIL | OVERRUN))
+ return 0;
+
+ /* Now try to overrun the board FIFO and get the overrun bit set:
+ */
+ status = overrun(ctlr);
+
+ if ((status & OVERRUN) == 0) /* No overrun bit set? */
+ return 0;
+
+ /* Assume we have a board.
+ */
+ reset(ctlr);
+
+ if ( (labpcs[unit] = malloc(sizeof(struct ctlr), M_DEVBUF, M_NOWAIT)) )
+ {
+ struct ctlr *l = labpcs[unit];
+
+ bzero(l, sizeof(struct ctlr));
+ l->base = ctlr->base;
+ dev->id_unit = l->unit = unit;
+
+ l->kdc = kdc_template;
+ l->kdc.kdc_state = DC_IDLE;
+
+ if ((status & LABPCPLUS) == 0)
+ l->kdc.kdc_description = "National Instrument's LabPC+";
+ else
+ l->kdc.kdc_description = "National Instrument's LabPC";
+
+ unit++;
+ return 1;
+ }
+ else
+ {
+ printf("labpc%d: Can't malloc.\n", unit);
+ return 0;
+ }
+}
+
+/* attach: Set things in a normal state.
+ */
+int labpcattach(struct isa_device *dev)
+{
+ struct ctlr *ctlr = labpcs[dev->id_unit];
+ ctlr->sample_us = (1000000.0 / (double)LABPC_DEFAULT_HERZ) + .50;
+ reset(ctlr);
+ labpc_registerdev(dev);
+
+ ctlr->dcr_val = 0x80;
+ ctlr->dcr_is = 0x80;
+ loutb(DCR(ctlr), ctlr->dcr_val);
+
+ return 1;
+}
+
+int
+labpcdetach(struct isa_device *id)
+{
+ struct ctlr *ctlr = labpcs[id->id_unit];
+ CR_CLR(ctlr, 3);
+ reset(ctlr);
+ dev_detach(&ctlr->kdc);
+ return 0;
+}
+
+/* Null handlers:
+ */
+static void null_intr (struct ctlr *ctlr) { }
+static void null_start(struct ctlr *ctlr, long count) { }
+static void null_stop (struct ctlr *ctlr) { }
+
+static inline void
+trigger(struct ctlr *ctlr)
+{
+ CR_EXPR(ctlr, 2, |= SWTRIG);
+}
+
+static void
+ad_start(struct ctlr *ctlr, long count)
+{
+ if (!SWTRIGGERRED(ctlr)) {
+ int chan = CHAN(ctlr->dev);
+ CR_EXPR(ctlr, 1, &= ~SCANEN);
+ CR_EXPR(ctlr, 2, &= ~TBSEL);
+
+ MA(ctlr, chan);
+ GAIN(ctlr, ctlr->gains[chan]);
+
+ if (SCAN(ctlr->dev))
+ CR_EXPR(ctlr, 1, |= SCANEN);
+
+ loutb(AMODE(ctlr), 0x34);
+ loutb(A0DATA(ctlr), (u_char)((ctlr->sample_us & 0xff)));
+ loutb(A0DATA(ctlr), (u_char)((ctlr->sample_us >> 8)&0xff));
+ loutb(AMODE(ctlr), 0x70);
+
+ ad_clear(ctlr);
+ trigger(ctlr);
+ }
+
+ ctlr->tmo = ((count + 16) * (long)ctlr->sample_us * hz) / 1000000 + 1;
+}
+
+static void
+ad_interval_start(struct ctlr *ctlr, long count)
+{
+ int chan = CHAN(ctlr->dev);
+ int n_frames = count / (chan + 1);
+
+ if (!SWTRIGGERRED(ctlr)) {
+ CR_EXPR(ctlr, 1, &= ~SCANEN);
+ CR_EXPR(ctlr, 2, &= ~TBSEL);
+
+ MA(ctlr, chan);
+ GAIN(ctlr, ctlr->gains[chan]);
+
+ /* XXX: Is it really possible that you clear INTSCAN as
+ * the documentation says? That seems pretty unlikely.
+ */
+ CR_EXPR(ctlr, 4, &= ~INTSCAN); /* XXX: Is this possible? */
+
+ /* Program the sample interval counter to run as fast as
+ * possible.
+ */
+ loutb(AMODE(ctlr), 0x34);
+ loutb(A0DATA(ctlr), (u_char)(0x02));
+ loutb(A0DATA(ctlr), (u_char)(0x00));
+ loutb(AMODE(ctlr), 0x70);
+
+ /* Program the interval scanning counter to run at the sample
+ * frequency.
+ */
+ loutb(BMODE(ctlr), 0x74);
+ loutb(B1DATA(ctlr), (u_char)((ctlr->sample_us & 0xff)));
+ loutb(B1DATA(ctlr), (u_char)((ctlr->sample_us >> 8)&0xff));
+ CR_EXPR(ctlr, 1, |= SCANEN);
+
+ ad_clear(ctlr);
+ trigger(ctlr);
+ }
+
+ /* Each frame time takes two microseconds per channel times
+ * the number of channels being sampled plus the sample period.
+ */
+ ctlr->tmo = ((n_frames + 16) *
+ ((long)ctlr->sample_us + (chan + 1 ) * 2 ) * hz) / 1000000 + 1;
+}
+
+static void
+all_stop(struct ctlr *ctlr)
+{
+ reset(ctlr);
+}
+
+static void
+tmo_stop(void *p)
+{
+ struct ctlr *ctlr = (struct ctlr *)p;
+ struct buf *bp;
+ int s = spltty();
+
+ if (ctlr == 0)
+ {
+ printf("labpc?: Null ctlr struct?\n");
+ return;
+ }
+
+ printf("labpc%d: timeout\n", ctlr->unit);
+
+ (*ctlr->stop)(ctlr);
+
+ bp = ctlr->start_queue.b_actf;
+
+ if (bp == 0) {
+ printf("labpc%d timeout: Null bp.\n", ctlr->unit);
+
+ /* No more data being transferred.
+ */
+ return;
+ }
+
+ done_and_dequeu(ctlr, bp, ETIMEDOUT);
+ splx(s);
+}
+
+static void ad_intr(struct ctlr *ctlr)
+{
+ u_char status;
+
+ while ( (status = (inb(STATUS(ctlr)) & (DAVAIL|OVERRUN|OVERFLOW)) ) )
+ {
+ if ((status & (OVERRUN|OVERFLOW)))
+ {
+ struct buf *bp = ctlr->start_queue.b_actf;
+
+ printf("ad_intr: error: data %p, status %x",
+ ctlr->data, status);
+
+ if (status & OVERRUN)
+ printf(" OVERRUN");
+
+ if (status & OVERFLOW)
+ printf(" OVERFLOW");
+
+ printf("\n");
+
+ (*ctlr->stop)(ctlr);
+
+ /* There may not be a bp if the interrupt went off between
+ * frames, that is, when no process was ready to receive and
+ * we are using a mode that is driven by the sample clock.
+ */
+ if (bp)
+ {
+ done_and_dequeu(ctlr, bp, EIO);
+ return;
+ }
+ else
+ ctlr->err = status; /* Set overrun condition */
+ }
+ else /* FIFO interrupt */
+ {
+ if (ctlr->data)
+ {
+ *ctlr->data++ = inb(ADFIFO(ctlr));
+ if (ctlr->data == ctlr->data_end) /* Normal completion */
+ {
+ struct buf *bp = ctlr->start_queue.b_actf;
+
+ done_and_dequeu(ctlr, bp, 0);
+ return;
+ }
+ }
+ else /* Interrupt with no where to put the data. */
+ {
+ (void)inb(ADFIFO(ctlr));
+ ctlr->err = DROPPED_INPUT;
+ return;
+ }
+ }
+ }
+}
+
+void labpcintr(int unit)
+{
+ struct ctlr *ctlr = labpcs[unit];
+ (*ctlr->intr)(ctlr);
+}
+
+/* mode_change_needed: Return whether or not we can open again, or
+ * if the new mode is inconsistent with an already opened mode.
+ * We only permit multiple opens for digital I/O now.
+ */
+
+static int
+mode_change_needed(dev_t current, dev_t next)
+{
+ return ! (DIGITAL(current) && DIGITAL(next));
+}
+
+int
+labpcopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ u_short unit = UNIT(dev);
+
+ struct ctlr *ctlr;
+
+ if (unit >= MAX_UNITS)
+ return ENXIO;
+
+ ctlr = labpcs[unit];
+
+ if (ctlr == 0)
+ return ENXIO;
+
+ /* Don't allow another open if we have to change modes.
+ */
+
+ if ( (ctlr->flags & BUSY) == 0)
+ {
+ ctlr->flags |= BUSY;
+ ctlr->kdc.kdc_state = DC_BUSY;
+
+ reset(ctlr);
+
+ ctlr->err = 0;
+ ctlr->dev = dev;
+
+ ctlr->intr = null_intr;
+ ctlr->starter = null_start;
+ ctlr->stop = null_stop;
+
+ CR_EXPR(ctlr, 3, |= ERRINTEN);
+ }
+ else if (mode_change_needed(ctlr->dev, dev))
+ return EBUSY;
+
+ return 0;
+}
+
+int
+labpcclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct ctlr *ctlr = labpcs[UNIT(dev)];
+
+ (*ctlr->stop)(ctlr);
+
+ ctlr->kdc.kdc_state = DC_IDLE;
+ ctlr->flags &= ~BUSY;
+
+ return 0;
+}
+
+/* Start: Start a frame going in or out.
+ */
+static void
+start(struct ctlr *ctlr)
+{
+ struct buf *bp;
+
+ if ((bp = ctlr->start_queue.b_actf) == 0)
+ {
+ /* We must turn off FIFO interrupts when there is no
+ * place to put the data. We have to get back to
+ * reading before the FIFO overflows.
+ */
+ CR_EXPR(ctlr, 3, &= ~FIFOINTEN);
+ ctlr->start_queue.b_active = 0;
+ return;
+ }
+
+ ctlr->data = (u_char *)bp->b_un.b_addr;
+ ctlr->data_end = ctlr->data + bp->b_bcount;
+
+ if (!FIFOINTENABLED(ctlr)) /* We can store the data again */
+ {
+ if (ctlr->err) /* Dropped input between records */
+ {
+ done_and_dequeu(ctlr, bp, ENOSPC);
+ return;
+ }
+ CR_EXPR(ctlr, 3, |= FIFOINTEN);
+ }
+ ctlr->err = 0;
+
+ (*ctlr->starter)(ctlr, bp->b_bcount);
+
+ timeout(tmo_stop, ctlr, ctlr->tmo);
+}
+
+static void
+ad_strategy(struct buf *bp, struct ctlr *ctlr)
+{
+ int s;
+
+ s = spltty();
+ bp->b_actf = NULL;
+
+ if (ctlr->start_queue.b_active)
+ {
+ ctlr->last->b_actf = bp;
+ ctlr->last = bp;
+ }
+ else
+ {
+ ctlr->start_queue.b_active = 1;
+ ctlr->start_queue.b_actf = bp;
+ ctlr->last = bp;
+ start(ctlr);
+ }
+ splx(s);
+}
+
+/* da_strategy: Send data to the D-A. The CHAN field should be
+ * 0: D-A port 0
+ * 1: D-A port 1
+ * 2: Alternate port 0 then port 1
+ *
+ * XXX:
+ *
+ * 1. There is no state for CHAN field 2:
+ * the first sample in each buffer goes to channel 0.
+ *
+ * 2. No interrupt support yet.
+ */
+void da_strategy(struct buf *bp, struct ctlr *ctlr)
+{
+ int len;
+ u_char *data;
+ int port;
+ int i;
+
+ switch(CHAN(bp->b_dev))
+ {
+ case 0:
+ port = DAC0L(ctlr);
+ break;
+
+ case 1:
+ port = DAC1L(ctlr);
+ break;
+
+ case 2: /* Device 2 handles both ports interleaved. */
+ if (bp->b_bcount <= 2)
+ {
+ port = DAC0L(ctlr);
+ break;
+ }
+
+ len = bp->b_bcount / 2;
+ data = (u_char *)bp->b_un.b_addr;
+
+ for (i = 0; i < len; i++)
+ {
+ loutb(DAC0H(ctlr), *data++);
+ loutb(DAC0L(ctlr), *data++);
+ loutb(DAC1H(ctlr), *data++);
+ loutb(DAC1L(ctlr), *data++);
+ }
+
+ bp->b_resid = bp->b_bcount & 3;
+ bp_done(bp, 0);
+ return;
+
+ default:
+ bp_done(bp, ENXIO);
+ return;
+ }
+
+ /* Port 0 or 1 falls through to here.
+ */
+ if (bp->b_bcount & 1) /* Odd transfers are illegal */
+ bp_done(bp, EIO);
+
+ len = bp->b_bcount;
+ data = (u_char *)bp->b_un.b_addr;
+
+ for (i = 0; i < len; i++)
+ {
+ loutb(port + 1, *data++);
+ loutb(port, *data++);
+ }
+
+ bp->b_resid = 0;
+
+ bp_done(bp, 0);
+}
+
+/* Input masks for MODE 0 of the ports treating PC as a single
+ * 8 bit port. Set these bits to set the port to input.
+ */
+ /* A B lowc highc combined */
+static u_char set_input[] = { 0x10, 0x02, 0x01, 0x08, 0x09 };
+
+static void flush_dcr(struct ctlr *ctlr)
+{
+ if (ctlr->dcr_is != ctlr->dcr_val)
+ {
+ loutb(DCR(ctlr), ctlr->dcr_val);
+ ctlr->dcr_is = ctlr->dcr_val;
+ }
+}
+
+/* do: Digital output
+ */
+static void
+digital_out_strategy(struct buf *bp, struct ctlr *ctlr)
+{
+ int len;
+ u_char *data;
+ int port;
+ int i;
+ int chan = CHAN(bp->b_dev);
+
+ ctlr->dcr_val &= ~set_input[chan]; /* Digital out: Clear bit */
+ flush_dcr(ctlr);
+
+ port = PORTX(ctlr, chan);
+
+ len = bp->b_bcount;
+ data = (u_char *)bp->b_un.b_addr;
+
+ for (i = 0; i < len; i++)
+ {
+ loutb(port, *data++);
+ }
+
+ bp->b_resid = 0;
+
+ bp_done(bp, 0);
+}
+
+/* digital_in_strategy: Digital input
+ */
+static void
+digital_in_strategy(struct buf *bp, struct ctlr *ctlr)
+{
+ int len;
+ u_char *data;
+ int port;
+ int i;
+ int chan = CHAN(bp->b_dev);
+
+ ctlr->dcr_val |= set_input[chan]; /* Digital in: Set bit */
+ flush_dcr(ctlr);
+ port = PORTX(ctlr, chan);
+
+ len = bp->b_bcount;
+ data = (u_char *)bp->b_un.b_addr;
+
+ for (i = 0; i < len; i++)
+ {
+ *data++ = inb(port);
+ }
+
+ bp->b_resid = 0;
+
+ bp_done(bp, 0);
+}
+
+
+void
+labpcstrategy(struct buf *bp)
+{
+ struct ctlr *ctlr = labpcs[UNIT(bp->b_dev)];
+
+ if (DIGITAL(bp->b_dev)) {
+ if (bp->b_flags & B_READ) {
+ ctlr->starter = null_start;
+ ctlr->stop = all_stop;
+ ctlr->intr = null_intr;
+ digital_in_strategy(bp, ctlr);
+ }
+ else
+ {
+ ctlr->starter = null_start;
+ ctlr->stop = all_stop;
+ ctlr->intr = null_intr;
+ digital_out_strategy(bp, ctlr);
+ }
+ }
+ else {
+ if (bp->b_flags & B_READ) {
+
+ ctlr->starter = INTERVAL(ctlr->dev) ? ad_interval_start : ad_start;
+ ctlr->stop = all_stop;
+ ctlr->intr = ad_intr;
+ ad_strategy(bp, ctlr);
+ }
+ else
+ {
+ ctlr->starter = null_start;
+ ctlr->stop = all_stop;
+ ctlr->intr = null_intr;
+ da_strategy(bp, ctlr);
+ }
+ }
+}
+
+int
+labpcioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc *p)
+{
+ struct ctlr *ctlr = labpcs[UNIT(dev)];
+
+ switch(cmd)
+ {
+ case AD_MICRO_PERIOD_SET:
+ {
+ /* XXX I'm only supporting what I have to, which is
+ * no slow periods. You can't get any slower than 15 Hz
+ * with the current setup. To go slower you'll need to
+ * support TCINTEN in CR3.
+ */
+
+ long sample_us = *(long *)arg;
+
+ if (sample_us > 65535)
+ return EIO;
+
+ ctlr->sample_us = sample_us;
+ return 0;
+ }
+
+ case AD_MICRO_PERIOD_GET:
+ *(long *)arg = ctlr->sample_us;
+ return 0;
+
+ case AD_NGAINS_GET:
+ *(int *)arg = 8;
+ return 0;
+
+ case AD_NCHANS_GET:
+ *(int *)arg = 8;
+ return 0;
+
+ case AD_SUPPORTED_GAINS:
+ {
+ static double gains[] = {1., 1.25, 2., 5., 10., 20., 50., 100.};
+ copyout(gains, *(caddr_t *)arg, sizeof(gains));
+
+ return 0;
+ }
+
+ case AD_GAINS_SET:
+ {
+ copyin(*(caddr_t *)arg, ctlr->gains, sizeof(ctlr->gains));
+ return 0;
+ }
+
+ case AD_GAINS_GET:
+ {
+ copyout(ctlr->gains, *(caddr_t *)arg, sizeof(ctlr->gains));
+ return 0;
+ }
+
+ default:
+ return ENOTTY;
+ }
+}
OpenPOWER on IntegriCloud