/* * 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 "opt_debug_outb.h" #include "opt_devfs.h" #include #include #include #include #include #define b_actf b_act.tqe_next #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include /* Miniumum timeout: */ #ifndef LABPC_MIN_TMO #define LABPC_MIN_TMO (hz) #endif #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 */ long min_tmo; /* Timeout in Herz */ int cleared_intr; 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; /* * Handle for canceling our timeout. */ struct callout_handle ch; /* Device configuration structure: */ #ifdef DEVFS void *devfs_token; #endif }; #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 static 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) static int labpcattach(struct isa_device *dev); static int labpcprobe(struct isa_device *dev); struct isa_driver labpcdriver = { labpcprobe, labpcattach, "labpc", 0 }; static d_read_t labpcread; static d_write_t labpcwrite; static d_open_t labpcopen; static d_close_t labpcclose; static d_ioctl_t labpcioctl; static d_strategy_t labpcstrategy; #define CDEV_MAJOR 66 static struct cdevsw labpc_cdevsw = { labpcopen, labpcclose, labpcread, labpcwrite, labpcioctl, nostop, nullreset, nodevtotty, seltrue, nommap, labpcstrategy, "labpc", NULL, -1 }; static void start(struct ctlr *ctlr); static void bp_done(struct buf *bp, int err) { bp->b_error = err; if (err || bp->b_resid) { bp->b_flags |= B_ERROR; } biodone(bp); } static void tmo_stop(void *p); static void done_and_start_next(struct ctlr *ctlr, struct buf *bp, int err) { 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, ctlr->ch); 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); } /* 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; } static 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; unit++; return 0x20; } else { printf("labpc%d: Can't malloc.\n", unit); return 0; } } /* attach: Set things in a normal state. */ static int labpcattach(struct isa_device *dev) { struct ctlr *ctlr = labpcs[dev->id_unit]; callout_handle_init(&ctlr->ch); ctlr->sample_us = (1000000.0 / (double)LABPC_DEFAULT_HERZ) + .50; reset(ctlr); ctlr->min_tmo = LABPC_MIN_TMO; ctlr->dcr_val = 0x80; ctlr->dcr_is = 0x80; loutb(DCR(ctlr), ctlr->dcr_val); #ifdef DEVFS ctlr->devfs_token = devfs_add_devswf(&labpc_cdevsw, 0, DV_CHR, /* what UID GID PERM */ 0, 0, 0600, "labpc%d", dev->id_unit); #endif return 1; } /* 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 + ctlr->min_tmo; } 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 + ctlr->min_tmo; } 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"); splx(s); return; } printf("labpc%d: timeout", ctlr->unit); (*ctlr->stop)(ctlr); bp = ctlr->start_queue.b_actf; if (bp == 0) { printf(", Null bp.\n"); splx(s); return; } printf("\n"); done_and_start_next(ctlr, bp, ETIMEDOUT); splx(s); } static void ad_intr(struct ctlr *ctlr) { u_char status; if (ctlr->cr_image[2] == 0) { if (ctlr->cleared_intr) { ctlr->cleared_intr = 0; return; } printf("ad_intr (should not happen) interrupt with interrupts off\n"); printf("status %x, cr3 %x\n", inb(STATUS(ctlr)), ctlr->cr_image[2]); return; } while ( (status = (inb(STATUS(ctlr)) & (DAVAIL|OVERRUN|OVERFLOW)) ) ) { if ((status & (OVERRUN|OVERFLOW))) { struct buf *bp = ctlr->start_queue.b_actf; printf("ad_intr: error: bp %p, data %p, status %x", (void *)bp, (void *)ctlr->data, status); if (status & OVERRUN) printf(" Conversion overrun (multiple A-D trigger)"); if (status & OVERFLOW) printf(" FIFO overflow"); printf("\n"); if (bp) { done_and_start_next(ctlr, bp, EIO); return; } else { printf("ad_intr: (should not happen) error between records\n"); ctlr->err = status; /* Set overrun condition */ return; } } else /* FIFO interrupt */ { struct buf *bp = ctlr->start_queue.b_actf; if (ctlr->data) { *ctlr->data++ = inb(ADFIFO(ctlr)); if (ctlr->data == ctlr->data_end) /* Normal completion */ { done_and_start_next(ctlr, bp, 0); return; } } else /* Interrupt with no where to put the data. */ { printf("ad_intr: (should not happen) dropped input.\n"); (void)inb(ADFIFO(ctlr)); printf("bp %p, status %x, cr3 %x\n", (void *)bp, status, ctlr->cr_image[2]); ctlr->err = DROPPED_INPUT; return; } } } } void labpcintr(int unit) { struct ctlr *ctlr = labpcs[unit]; (*ctlr->intr)(ctlr); } /* lockout_multiple_opens: 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 lockout_multiple_open(dev_t current, dev_t next) { return ! (DIGITAL(current) && DIGITAL(next)); } static 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; reset(ctlr); ctlr->err = 0; ctlr->dev = dev; ctlr->intr = null_intr; ctlr->starter = null_start; ctlr->stop = null_stop; } else if (lockout_multiple_open(ctlr->dev, dev)) return EBUSY; return 0; } static int labpcclose(dev_t dev, int flags, int fmt, struct proc *p) { struct ctlr *ctlr = labpcs[UNIT(dev)]; (*ctlr->stop)(ctlr); ctlr->flags &= ~BUSY; return 0; } static int labpcread( dev_t dev, struct uio *uio, int ioflag) { return (physio(labpcstrategy, NULL, dev, 1, minphys, uio)); } static int labpcwrite ( dev_t dev, struct uio *uio, int ioflag) { return (physio(labpcstrategy, NULL, dev, 0, minphys, uio)); } /* * 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|ERRINTEN)); ctlr->cleared_intr = 1; ctlr->start_queue.b_bcount = 0; return; } ctlr->data = (u_char *)bp->b_data; ctlr->data_end = ctlr->data + bp->b_bcount; if (ctlr->err) { printf("labpc start: (should not happen) error between records.\n"); done_and_start_next(ctlr, bp, EIO); return; } if (ctlr->data == 0) { printf("labpc start: (should not happen) NULL data pointer.\n"); done_and_start_next(ctlr, bp, EIO); return; } (*ctlr->starter)(ctlr, bp->b_bcount); if (!FIFOINTENABLED(ctlr)) /* We can store the data again */ { CR_EXPR(ctlr, 3, |= (FIFOINTEN|ERRINTEN)); /* Don't wait for the interrupts to fill things up. */ (*ctlr->intr)(ctlr); } ctlr->ch = 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_bcount) { ctlr->last->b_actf = bp; ctlr->last = bp; } else { ctlr->start_queue.b_bcount = 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. */ static 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_data; 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_data; 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_data; 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_data; for (i = 0; i < len; i++) { *data++ = inb(port); } bp->b_resid = 0; bp_done(bp, 0); } static 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); } } } static int labpcioctl(dev_t dev, u_long 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; } } static labpc_devsw_installed = 0; static void labpc_drvinit(void *unused) { dev_t dev; if( ! labpc_devsw_installed ) { dev = makedev(CDEV_MAJOR,0); cdevsw_add(&dev,&labpc_cdevsw,NULL); labpc_devsw_installed = 1; } } SYSINIT(labpcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,labpc_drvinit,NULL)