diff options
author | phk <phk@FreeBSD.org> | 1998-04-05 19:26:08 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 1998-04-05 19:26:08 +0000 |
commit | b3e4247d70bf104d92ef7b0cafe37d9e4e3ada4f (patch) | |
tree | 312dce14c21558f278412afbb92bd550f115bb41 /sys/i386/isa/loran.c | |
parent | cf4f0083bb799d8dcf25750600e78fc49ed280d7 (diff) | |
download | FreeBSD-src-b3e4247d70bf104d92ef7b0cafe37d9e4e3ada4f.zip FreeBSD-src-b3e4247d70bf104d92ef7b0cafe37d9e4e3ada4f.tar.gz |
Commit a much more functional version of this driver.
Diffstat (limited to 'sys/i386/isa/loran.c')
-rw-r--r-- | sys/i386/isa/loran.c | 481 |
1 files changed, 460 insertions, 21 deletions
diff --git a/sys/i386/isa/loran.c b/sys/i386/isa/loran.c index 8149067..a255525 100644 --- a/sys/i386/isa/loran.c +++ b/sys/i386/isa/loran.c @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - * $Id: loran.c,v 1.1 1998/02/24 22:05:12 phk Exp $ + * $Id: loran.c,v 1.2 1998/03/28 13:24:31 bde Exp $ * * This device-driver helps the userland controlprogram for a LORAN-C * receiver avoid monopolizing the CPU. @@ -20,81 +20,519 @@ * */ +#ifdef KERNEL +#include "loran.h" +#include "opt_devfs.h" + #include <sys/param.h> #include <sys/systm.h> +#include <sys/sysctl.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/uio.h> +#include <sys/syslog.h> +#include <sys/time.h> +#include <sys/malloc.h> +#ifdef DEVFS +#include <sys/devfsext.h> +#endif /*DEVFS*/ #include <i386/isa/isa_device.h> +#endif /* KERNEL */ -static int loranprobe (struct isa_device *dvp); -static int loranattach (struct isa_device *isdp); +struct datapoint { + void *ident; + int index; + u_int64_t scheduled; + u_int delay; + u_int code; + u_int gri; + u_int agc; + u_int phase; + u_int par; + u_int isig; + u_int qsig; + u_int ssig; + u_int64_t epoch; + struct timespec actual; + TAILQ_ENTRY(datapoint) list; + double ival; + double qval; + double mval; +}; -struct isa_driver lorandriver = { - loranprobe, loranattach, "loran" +/* + * Mode register (PAR) hardware definitions + */ + #define INTEG 0x03 /* integrator mask */ + #define INTEG_1000us 0 + #define INTEG_264us 1 + #define INTEG_36us 2 + #define INTEG_SHORT 3 + #define GATE 0x0C /* gate source mask */ + #define GATE_OPEN 0x4 + #define GATE_GRI 0x8 + #define GATE_PCI 0xc + #define GATE_STB 0xf + #define MSB 0x10 /* load dac high-order bits */ + #define IEN 0x20 /* enable interrupt bit */ + #define EN5 0x40 /* enable counter 5 bit */ + #define ENG 0x80 /* enable gri bit */ + + +#ifdef KERNEL + +#define VCO 2048 /* initial vco dac (0 V)*/ + +#define PORT 0x0300 /* controller port address */ + +#define PGUARD 990 /* program guard time (cycle) (990!) */ + +#define GRI 800 /* pulse-group gate (cycle) */ + +/* + * Analog/digital converter (ADC) hardware definitions + */ +#define ADC PORT+2 /* adc buffer (r)/address (w) */ +#define ADCGO PORT+3 /* adc status (r)/adc start (w) */ + #define ADC_START 0x01 /* converter start bit (w) */ + #define ADC_BUSY 0x01 /* converter busy bit (r) */ + #define ADC_DONE 0x80 /* converter done bit (r) */ + #define ADC_I 0 /* i channel (phase) */ + #define ADC_Q 1 /* q channel (amplitude) */ + #define ADC_S 2 /* s channel (agc) */ + +/* + * Digital/analog converter (DAC) hardware definitions + * Note: output voltage increases with value programmed; the buffer + * is loaded in two 8-bit bytes, the lsb 8 bits with the MSB bit off in + * the PAR register, the msb 4 bits with the MSB on. + */ +#define DACA PORT+4 /* vco (dac a) buffer (w) */ +#define DACB PORT+5 /* agc (dac b) buffer (w) */ + +/* + * Pulse-code generator (CODE) hardware definitions + * Note: bits are shifted out from the lsb first + */ +#define CODE PORT+6 /* pulse-code buffer (w) */ + #define MPCA 0xCA /* LORAN-C master pulse code group a */ + #define MPCB 0x9F /* LORAN-C master pulse code group b */ + #define SPCA 0xF9 /* LORAN-C slave pulse code group a */ + #define SPCB 0xAC /* LORAN-C slave pulse code group b */ + +/* + * Mode register (PAR) hardware definitions + */ +#define PAR PORT+7 /* parameter buffer (w) */ + +#define TGC PORT+0 /* stc control port (r/w) */ +#define TGD PORT+1 /* stc data port (r/w) */ + +/* + * Timing generator (STC) hardware commands + */ +/* argument sssss = counter numbers 5-1 */ +#define TG_LOADDP 0x00 /* load data pointer */ + /* argument ee = element (all groups except ggg = 000 or 111) */ + #define MODEREG 0x00 /* mode register */ + #define LOADREG 0x08 /* load register */ + #define HOLDREG 0x10 /* hold register */ + #define HOLDINC 0x18 /* hold register (hold cycle increm) */ + /* argument ee = element (group ggg = 111) */ + #define ALARM1 0x07 /* alarm register 1 */ + #define ALARM2 0x0F /* alarm register 2 */ + #define MASTER 0x17 /* master mode register */ + #define STATUS 0x1F /* status register */ +#define ARM 0x20 /* arm counters */ +#define LOAD 0x40 /* load counters */ +#define TG_LOADARM 0x60 /* load and arm counters */ +#define DISSAVE 0x80 /* disarm and save counters */ +#define TG_SAVE 0xA0 /* save counters */ +#define DISARM 0xC0 /* disarm counters */ +/* argument nnn = counter number */ +#define SETTOG 0xE8 /* set toggle output HIGH for counter */ +#define CLRTOG 0xE0 /* set toggle output LOW for counter */ +#define STEP 0xF0 /* step counter */ +/* argument eeggg, where ee = element, ggg - counter group */ +/* no arguments */ +#define ENABDPS 0xE0 /* enable data pointer sequencing */ +#define ENABFOUT 0xE6 /* enable fout */ +#define ENAB8 0xE7 /* enable 8-bit data bus */ +#define DSABDPS 0xE8 /* disable data pointer sequencing */ +#define ENAB16 0xEF /* enable 16-bit data bus */ +#define DSABFOUT 0xEE /* disable fout */ +#define ENABPFW 0xF8 /* enable prefetch for write */ +#define DSABPFW 0xF9 /* disable prefetch for write */ +#define TG_RESET 0xFF /* master reset */ + + +#define NENV 40 /* size of envelope filter */ +#define CLOCK 50 /* clock period (clock) */ +#define CYCLE 10 /* carrier period (us) */ +#define PCX (NENV * CLOCK) /* envelope gate (clock) */ +#define STROBE 50 /* strobe gate (clock) */ + +u_short tg_init[] = { /* stc initialization vector */ + 0x0562, 12, 13, /* counter 1 (p0) */ + 0x0262, PGUARD, GRI, /* counter 2 (gri) */ + 0x8562, PCX, 5000 - PCX, /* counter 3 (pcx) */ + 0xc562, 0, STROBE, /* counter 4 (stb) */ + 0x052a, 0, 0 /* counter 5 (out) */ }; -struct timespec loran_token; +/**********************************************************************/ + +static TAILQ_HEAD(qhead, datapoint) qdone, qready; + +static struct datapoint dummy; + +static u_int64_t ticker; + +static u_char par; + +static struct datapoint *this, *next; +static MALLOC_DEFINE(M_LORAN, "Loran", "Loran datapoints"); + +static int loranerror; +static char lorantext[40]; + +/**********************************************************************/ + +static int loranprobe (struct isa_device *dvp); +static int loranattach (struct isa_device *isdp); +static void loranenqueue (struct datapoint *); static d_open_t loranopen; static d_close_t loranclose; static d_read_t loranread; +static d_write_t loranwrite; +static struct timecounter loran_timecounter[]; -#define CDEV_MAJOR 94 -static struct cdevsw loran_cdevsw = - { loranopen, loranclose, loranread, nowrite, - noioctl, nullstop, nullreset, nodevtotty, - seltrue, nommap, nostrat, "loran", - NULL, -1 }; - +/**********************************************************************/ int loranprobe(struct isa_device *dvp) { - dvp->id_iobase = 0x300; + /* We need to be a "fast-intr" */ + dvp->id_ri_flags |= RI_FAST; + + dvp->id_iobase = PORT; return (8); } int loranattach(struct isa_device *isdp) { + int i; + + /* We need to be a "fast-intr" */ + isdp->id_ri_flags |= RI_FAST; + printf("loran0: LORAN-C Receiver\n"); + + /* Initialize the 9513A */ + outb(TGC, TG_RESET); outb(TGC, LOAD+0x1f); /* reset STC chip */ + outb(TGC, TG_LOADDP+MASTER); outb(TGD, 0xf0); outb(TGD, 0x8a); + outb(TGC, TG_LOADDP+1); + tg_init[4] = 7499 - GRI; + for (i = 0; i < 5*3; i++) { + outb(TGD, tg_init[i]); + outb(TGD, tg_init[i] >> 8); + } + outb(TGC, TG_LOADARM+0x1f); /* let the good times roll */ + + /* Load the VCO DAC */ + outb(PAR, 0); outb(DACA, VCO & 0xff); + outb(PAR, MSB); outb(DACA, VCO >> 8); + + init_timecounter(loran_timecounter); + + TAILQ_INIT(&qdone); + TAILQ_INIT(&qready); + + dummy.agc = 2000; + dummy.code = 0x55; + dummy.delay = PGUARD - GRI; + + TAILQ_INSERT_HEAD(&qready, &dummy, list); + this = &dummy; + next = &dummy; + + inb(ADC); /* Flush any old result */ + outb(ADC, ADC_S); + + par = ENG|IEN; + outb(PAR, par); + return (1); } static int loranopen (dev_t dev, int flags, int fmt, struct proc *p) { + u_long ef; + struct datapoint *this; + while (!TAILQ_EMPTY(&qdone)) { + ef = read_eflags(); + disable_intr(); + this = TAILQ_FIRST(&qdone); + TAILQ_REMOVE(&qdone, this, list); + write_eflags(ef); + FREE(this, M_LORAN); + } + loranerror = 0; return(0); } static int loranclose(dev_t dev, int flags, int fmt, struct proc *p) { - + /* + * Lower ENG + */ return(0); } static int loranread(dev_t dev, struct uio * uio, int ioflag) { - int err, c; + u_long ef; + struct datapoint *this; + int err, c; + + if (loranerror) { + printf("Loran0: %s", lorantext); + return(EIO); + } + if (TAILQ_EMPTY(&qdone)) + tsleep ((caddr_t)&qdone, PZERO + 8 |PCATCH, "loranrd", hz*2); + if (TAILQ_EMPTY(&qdone)) + return(0); + this = TAILQ_FIRST(&qdone); + ef = read_eflags(); + disable_intr(); + TAILQ_REMOVE(&qdone, this, list); + write_eflags(ef); + + c = imin(uio->uio_resid, (int)sizeof *this); + err = uiomove((caddr_t)this, c, uio); + FREE(this, M_LORAN); + return(err); +} + +static void +loranenqueue(struct datapoint *this) +{ + struct datapoint *p, *q; + u_long ef; + u_int64_t x; + + if (this->scheduled < ticker) { + x = (ticker - this->scheduled) / (2 * this->gri); + this->scheduled += x * 2 * this->gri; + } - tsleep ((caddr_t)&loran_token, PZERO + 8 |PCATCH, "loranrd", hz*10); - c = imin(uio->uio_resid, (int)sizeof loran_token); - err = uiomove((caddr_t)&loran_token, c, uio); + ef = read_eflags(); + disable_intr(); + + p = TAILQ_FIRST(&qready); + while (1) { + while (this->scheduled < p->scheduled + PGUARD) + this->scheduled += 2 * this->gri; + q = TAILQ_NEXT(p, list); + if (!q) { + this->delay = this->scheduled - p->scheduled - GRI; + TAILQ_INSERT_TAIL(&qready, this, list); + break; + } + if (this->scheduled + PGUARD < q->scheduled) { + this->delay = this->scheduled - p->scheduled - GRI; + TAILQ_INSERT_BEFORE(q, this, list); + q->delay = q->scheduled - this->scheduled - GRI; + break; + } + p = q; + } + write_eflags(ef); +} + +static int +loranwrite(dev_t dev, struct uio * uio, int ioflag) +{ + int err = 0, c; + struct datapoint *this; + + MALLOC(this, struct datapoint *, sizeof *this, M_LORAN, M_WAITOK); + c = imin(uio->uio_resid, (int)sizeof *this); + err = uiomove((caddr_t)this, c, uio); + if (!err && this->gri == 0) + err = EINVAL; + if (!err) + loranenqueue(this); + else + FREE(this, M_LORAN); return(err); } void loranintr(int unit) { - nanotime(&loran_token); - wakeup((caddr_t)&loran_token); + u_long ef; + int status, count = 0; + + ef = read_eflags(); + disable_intr(); + + this->ssig = inb(ADC); + + par &= ~(ENG | IEN); + outb(PAR, par); + + outb(ADC, ADC_I); + outb(ADCGO, ADC_START); + while (!(inb(ADCGO) & ADC_DONE)) + continue; + this->isig = inb(ADC); + + outb(ADC, ADC_Q); + outb(ADCGO, ADC_START); + while (!(inb(ADCGO) & ADC_DONE)) + continue; + this->qsig = inb(ADC); + + outb(ADC, ADC_S); + + this->epoch = ticker; + + if (this != &dummy) { + nanotime(&this->actual); /* XXX */ + TAILQ_INSERT_TAIL(&qdone, this, list); + wakeup((caddr_t)&qdone); + } + + if (next != &dummy || TAILQ_NEXT(next, list)) + TAILQ_REMOVE(&qready, next, list); + + this = next; + ticker += GRI; + ticker += this->delay; + + next = TAILQ_FIRST(&qready); + if (!next) { + next = &dummy; + TAILQ_INSERT_HEAD(&qready, next, list); + } else if (next->delay + GRI > PGUARD * 2) { + next->delay -= PGUARD; + next = &dummy; + TAILQ_INSERT_HEAD(&qready, next, list); + } + if (next == &dummy) + next->scheduled = ticker + GRI + next->delay; + + /* load this->params */ + par &= ~(INTEG|GATE); + par |= this->par; + + par &= ~MSB; outb(PAR, par); outb(DACB, this->agc); + par |= MSB; outb(PAR, par); outb(DACB, this->agc>>8); + + switch (this->code) { + case 256+0: outb(CODE, MPCA); break; + case 256+1: outb(CODE, MPCB); break; + case 256+2: outb(CODE, SPCA); break; + case 256+3: outb(CODE, SPCB); break; + default: outb(CODE, this->code); break; + } + + outb(TGC, TG_LOADDP + 0x0c); + outb(TGD, this->phase); + outb(TGD, this->phase >> 8); + + /* load next->delay into 9513 */ + outb(TGC, TG_LOADDP + 0x0a); + outb(TGD, next->delay); + outb(TGD, next->delay >> 8); + + + status = inb(TGC); + status &= 0x1c; + + if (status) { + outb(TGC, TG_SAVE + 2); /* save counter #2 */ + outb(TGC, TG_LOADDP +0x12); /* hold counter #2 */ + count = inb(TGD & 0xff); + count |= inb(TGD) << 8; + outb(TGC, TG_LOADDP +0x12); /* hold counter #2 */ + outb(TGD, GRI & 0xff); + outb(TGD, GRI >> 8); + } + + par |= ENG | IEN; + outb(PAR, par); + + if (status) { + sprintf(lorantext, "Missed: %02x %d %d\n", + status, count, next->delay); + loranerror = 1; + } + if (next->delay < PGUARD - GRI) { + sprintf(lorantext, "Bogus: %02x %d %d\n", + status, count, next->delay); + loranerror = 1; + } + + write_eflags(ef); +} + +/**********************************************************************/ + +static u_int64_t +loran_get_timecount(void) +{ + u_int32_t count; + u_long ef; + u_int high, low; + + ef = read_eflags(); + disable_intr(); + + outb(TGC, TG_SAVE + 0x10); /* save counter #5 */ + outb(TGC, TG_LOADDP +0x15); /* hold counter #5 */ + count = inb(TGD); + count |= inb(TGD) << 8; + + write_eflags(ef); + return (count); } +static struct timecounter loran_timecounter[3] = { + 0, /* get_timedelta */ + loran_get_timecount, /* get_timecount */ + 0xffff, /* counter_mask */ + 5000000, /* frequency */ + "loran" /* name */ +}; + +SYSCTL_OPAQUE(_debug, OID_AUTO, loran_timecounter, CTLFLAG_RD, + loran_timecounter, sizeof(loran_timecounter), "S,timecounter", ""); + + +/**********************************************************************/ + +struct isa_driver lorandriver = { + loranprobe, loranattach, "loran" +}; + +#define CDEV_MAJOR 94 +static struct cdevsw loran_cdevsw = + { loranopen, loranclose, loranread, loranwrite, + noioctl, nullstop, nullreset, nodevtotty, + seltrue, nommap, nostrat, "loran", + NULL, -1 }; + + static loran_devsw_installed = 0; static void loran_drvinit(void *unused) @@ -110,3 +548,4 @@ static void loran_drvinit(void *unused) SYSINIT(lorandev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,loran_drvinit,NULL) +#endif /* KERNEL */ |