summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>1998-10-24 19:55:09 +0000
committerphk <phk@FreeBSD.org>1998-10-24 19:55:09 +0000
commitbfaec8d4e5ddb15d7ab77116b37c8298166c4132 (patch)
treeb47a749e10e72a73315ed8d6fb5293f8d778d86f
parent14fa987f115eeb5e92e8e4fcb694c1e0694103d5 (diff)
downloadFreeBSD-src-bfaec8d4e5ddb15d7ab77116b37c8298166c4132.zip
FreeBSD-src-bfaec8d4e5ddb15d7ab77116b37c8298166c4132.tar.gz
Update to current reality. We can now track several LORSTA on separate
minor devices. Improve PLL/OCXO DAC dithering. General remodeling. Performance is now 2.5e-11 in frequency and +/- 100 nsec in time, both of which are actually the limits of the transmitted signal.
-rw-r--r--sys/i386/isa/loran.c321
1 files changed, 185 insertions, 136 deletions
diff --git a/sys/i386/isa/loran.c b/sys/i386/isa/loran.c
index 07aff88..8b16501 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.9 1998/10/22 05:58:39 bde Exp $
+ * $Id: loran.c,v 1.10 1998/10/23 10:45:10 phk Exp $
*
* This device-driver helps the userland controlprogram for a LORAN-C
* receiver avoid monopolizing the CPU.
@@ -32,13 +32,13 @@
#include <i386/isa/isa_device.h>
#endif /* KERNEL */
+typedef TAILQ_HEAD(, datapoint) dphead_t;
+
struct datapoint {
- void *ident;
- int index;
+ /* Fields used by kernel */
u_int64_t scheduled;
- u_int delay;
u_int code;
- u_int gri;
+ u_int fri;
u_int agc;
u_int phase;
u_int width;
@@ -47,16 +47,22 @@ struct datapoint {
u_int qsig;
u_int ssig;
u_int64_t epoch;
- struct timespec actual;
TAILQ_ENTRY(datapoint) list;
+ u_char status;
+ int vco;
+ pid_t pid;
+
+ int priority;
+ dphead_t *home;
+
+ /* Fields used only in userland */
+ void *ident;
+ int index;
double ival;
double qval;
double sval;
double mval;
- u_char status;
- u_int vco;
- int count;
- int remain;
+
};
/*
@@ -77,14 +83,21 @@ struct datapoint {
#define EN5 0x40 /* enable counter 5 bit */
#define ENG 0x80 /* enable gri bit */
-#define VCO 2048 /* initial vco dac (0 V)*/
+#define VCO_SHIFT 8 /* bits of fraction on VCO value */
+#define VCO (2048 << VCO_SHIFT) /* initial vco dac (0 V)*/
+
+
+#define PGUARD 990 /* program guard time (cycle) (990!) */
+
#ifdef KERNEL
+#define NLORAN 10 /* Allow ten minor devices */
+
+#define NDUMMY 4 /* How many idlers we want */
#define PORT 0x0300 /* controller port address */
-#define PGUARD 990 /* program guard time (cycle) (990!) */
#define GRI 800 /* pulse-group gate (cycle) */
@@ -183,9 +196,9 @@ struct datapoint {
/**********************************************************************/
-static TAILQ_HEAD(qhead, datapoint) qdone, qready;
+dphead_t minors[NLORAN], working, holding;
-static struct datapoint dummy;
+static struct datapoint dummy[NDUMMY];
static u_int64_t ticker;
@@ -198,8 +211,11 @@ static MALLOC_DEFINE(M_LORAN, "Loran", "Loran datapoints");
static int loranerror;
static char lorantext[80];
-static u_int vco_is;
-static u_int vco_should;
+static u_int vco_is;
+static u_int vco_should;
+static u_int vco_want;
+static u_int64_t vco_when;
+static int64_t vco_error;
static int lorantc_magic;
@@ -214,7 +230,7 @@ static d_close_t loranclose;
static d_read_t loranread;
static d_write_t loranwrite;
static ointhand2_t loranintr;
-extern struct timecounter loran_timecounter[];
+extern struct timecounter loran_timecounter;
/**********************************************************************/
@@ -229,10 +245,10 @@ loranprobe(struct isa_device *dvp)
}
u_short tg_init[] = { /* stc initialization vector */
- 0x0562, 12, 13, /* counter 1 (p0) */
- 0x0262, PGUARD, GRI, /* counter 2 (gri) */
+ 0x0562, 12, 13, /* counter 1 (p0) Mode J */
+ 0x0262, PGUARD, GRI, /* counter 2 (gri) Mode J */
0x8562, PCX, 5000 - PCX, /* counter 3 (pcx) */
- 0xc562, 0, STROBE, /* counter 4 (stb) */
+ 0xc562, 0, STROBE, /* counter 4 (stb) Mode L */
0x052a, 0, 0 /* counter 5 (out) */
};
@@ -265,26 +281,29 @@ loranattach(struct isa_device *isdp)
printf("loran0: LORAN-C Receiver\n");
- vco_is = VCO;
- LOAD_DAC(DACA, VCO);
+ vco_should = vco_is = VCO;
+ LOAD_DAC(DACA, vco_is >> VCO_SHIFT);
init_tgc();
init_timecounter(&loran_timecounter);
- TAILQ_INIT(&qdone);
- TAILQ_INIT(&qready);
-
- dummy.agc = 4095;
- dummy.code = 0xac;
- dummy.delay = PGUARD - GRI;
- dummy.gri = PGUARD;
- dummy.phase = 50;
- dummy.width = 50;
+ TAILQ_INIT(&working);
+ TAILQ_INIT(&holding);
+ for (i = 0; i < NLORAN; i++) {
+ TAILQ_INIT(&minors[i]);
+
+ }
- TAILQ_INSERT_HEAD(&qready, &dummy, list);
- this = &dummy;
- next = &dummy;
+ for (i = 0; i < NDUMMY; i++) {
+ dummy[i].agc = 4095;
+ dummy[i].code = 0xac;
+ dummy[i].fri = PGUARD;
+ dummy[i].phase = 50;
+ dummy[i].width = 50;
+ dummy[i].priority = 9999;
+ TAILQ_INSERT_TAIL(&working, &dummy[i], list);
+ }
inb(ADC); /* Flush any old result */
outb(ADC, ADC_S);
@@ -300,26 +319,18 @@ loranopen (dev_t dev, int flags, int fmt, struct proc *p)
{
u_long ef;
struct datapoint *this;
+ int idx;
+
+ idx = minor(dev);
+ if (idx >= NLORAN)
+ return (ENODEV);
- 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);
- }
- init_tgc();
- loranerror = 0;
return(0);
}
static int
loranclose(dev_t dev, int flags, int fmt, struct proc *p)
{
- /*
- * Lower ENG
- */
return(0);
}
@@ -329,19 +340,22 @@ loranread(dev_t dev, struct uio * uio, int ioflag)
u_long ef;
struct datapoint *this;
int err, c;
+ int idx;
+
+ idx = minor(dev);
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))
+ if (TAILQ_EMPTY(&minors[idx]))
+ tsleep ((caddr_t)&minors[idx], PZERO + 8 |PCATCH, "loranrd", hz*2);
+ if (TAILQ_EMPTY(&minors[idx]))
return(0);
- this = TAILQ_FIRST(&qdone);
+ this = TAILQ_FIRST(&minors[idx]);
ef = read_eflags();
disable_intr();
- TAILQ_REMOVE(&qdone, this, list);
+ TAILQ_REMOVE(&minors[idx], this, list);
write_eflags(ef);
c = imin(uio->uio_resid, (int)sizeof *this);
@@ -351,55 +365,114 @@ loranread(dev_t dev, struct uio * uio, int ioflag)
}
static void
-loranenqueue(struct datapoint *this)
+loranenqueue(struct datapoint *dp)
{
- 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;
- }
-
- 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;
+ struct datapoint *dpp, *dpn;
+
+ while(1) {
+ /*
+ * The first two elements on "working" are sacred,
+ * they're already partly setup in hardware, so the
+ * earliest slot we can use is #3
+ */
+ dpp = TAILQ_FIRST(&working);
+ dpp = TAILQ_NEXT(dpp, list);
+ dpn = TAILQ_NEXT(dpp, list);
+ while (1) {
+ /*
+ * We cannot bump "dpp", so if "dp" overlaps it
+ * skip a beat.
+ * XXX: should use better algorithm ?
+ */
+ if (dpp->scheduled + PGUARD > dp->scheduled) {
+ dp->scheduled += dp->fri;
+ continue;
+ }
+
+ /*
+ * If "dpn" will be done before "dp" wants to go,
+ * we must look further down the list.
+ */
+ if (dpn && dpn->scheduled + PGUARD < dp->scheduled) {
+ dpp = dpn;
+ dpn = TAILQ_NEXT(dpp, list);
+ continue;
+ }
+
+ /*
+ * If at end of list, put "dp" there
+ */
+ if (!dpn) {
+ TAILQ_INSERT_AFTER(&working, dpp, dp, list);
+ break;
+ }
+
+ /*
+ * If "dp" fits before "dpn", insert it there
+ */
+ if (dpn->scheduled > dp->scheduled + PGUARD) {
+ TAILQ_INSERT_AFTER(&working, dpp, dp, list);
+ break;
+ }
+
+ /*
+ * If "dpn" is less important, bump it.
+ */
+ if (dp->priority < dpn->priority) {
+ TAILQ_REMOVE(&working, dpn, list);
+ TAILQ_INSERT_TAIL(&holding, dpn, list);
+ dpn = TAILQ_NEXT(dpp, list);
+ continue;
+ }
+
+ /*
+ * "dpn" was more or equally important, "dp" must
+ * take yet turn.
+ */
+ dp->scheduled += dp->fri;
}
- 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;
+
+ /*
+ * If anything was bumped, put it back as best we can
+ */
+ if (TAILQ_EMPTY(&holding))
break;
- }
- p = q;
+ dp = TAILQ_FIRST(&holding);
+ TAILQ_REMOVE(&holding, dp, list);
}
- write_eflags(ef);
}
static int
loranwrite(dev_t dev, struct uio * uio, int ioflag)
{
+ u_long ef;
int err = 0, c;
struct datapoint *this;
+ int idx;
+ u_int64_t dt;
+
+ idx = minor(dev);
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)
+ if (!err && this->fri == 0)
err = EINVAL;
+ /* XXX more checks */
+ this->home = &minors[idx];
+ this->priority = idx;
+ if (ticker > this->scheduled) {
+ dt = ticker - this->scheduled;
+ dt -= dt % this->fri;
+ this->scheduled += dt;
+ }
if (!err) {
+ ef = read_eflags();
+ disable_intr();
loranenqueue(this);
- vco_should = this->vco;
+ write_eflags(ef);
+ if (this->vco >= 0)
+ vco_want = this->vco;
} else {
FREE(this, M_LORAN);
}
@@ -411,31 +484,14 @@ loranintr(int unit)
{
u_long ef;
int status = 0, count = 0, i;
+ struct datapoint *this, *next;
+ int delay;
ef = read_eflags();
disable_intr();
- if (this != &dummy) {
- outb(TGC, DSABDPS);
- outb(TGC, TG_LOADDP + 0x12); /* hold counter #2 */
- this->remain = -1;
- i = 2;
- for (i = 0; i < 2; i++) {
- count = this->remain;
- do {
- outb(TGC, TG_SAVE + 0x12);
- this->remain = inb(TGD) & 0xff;
- this->remain |= inb(TGD) << 8;
- } while (count == this->remain);
- }
- lorantc_magic = 1;
- nanotime(&this->actual);
- lorantc_magic = 0;
- outb(TGC, TG_LOADDP + 0x0a);
- this->count = inb(TGD);
- this->count |= inb(TGD) << 8;
- LOAD_9513(0x12, GRI)
- }
+ this = TAILQ_FIRST(&working);
+ TAILQ_REMOVE(&working, this, list);
this->ssig = inb(ADC);
@@ -458,29 +514,18 @@ loranintr(int unit)
this->epoch = ticker;
- if (this != &dummy) {
- TAILQ_INSERT_TAIL(&qdone, this, list);
- wakeup((caddr_t)&qdone);
+ if (this->home) {
+ TAILQ_INSERT_TAIL(this->home, this, list);
+ wakeup((caddr_t)this->home);
+ } else {
+ loranenqueue(this);
}
- 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;
+ this = TAILQ_FIRST(&working);
+ next = TAILQ_NEXT(this, list);
+
+ delay = next->scheduled - this->scheduled;
+ delay -= GRI;
/* load this->params */
par &= ~(INTEG|GATE);
@@ -490,7 +535,7 @@ loranintr(int unit)
outb(CODE, this->code);
- LOAD_9513(0x0a, next->delay);
+ LOAD_9513(0x0a, delay);
/*
* We need to load this from the opposite register * due to some
@@ -502,10 +547,17 @@ loranintr(int unit)
outb(TGC, TG_LOADARM + 0x08);
LOAD_9513(0x14, this->width);
- if (vco_is != vco_should) {
- LOAD_DAC(DACA, vco_should);
- vco_is = vco_should;
+ vco_error += ((vco_is << VCO_SHIFT) - vco_should) * (ticker - vco_when);
+ vco_should = vco_want;
+ i = vco_should >> VCO_SHIFT;
+ if (vco_error < 0)
+ i++;
+
+ if (vco_is != i) {
+ LOAD_DAC(DACA, i);
+ vco_is = i;
}
+ vco_when = ticker;
this->status = inb(TGC);
#if 1
@@ -526,15 +578,12 @@ loranintr(int unit)
if (status) {
sprintf(lorantext, "Missed: %02x %d %d this:%p next:%p (dummy=%p)\n",
- status, count, next->delay, this, next, &dummy);
- loranerror = 1;
- }
- if (next->delay < PGUARD - GRI) {
- sprintf(lorantext, "Bogus: %02x %d %d\n",
- status, count, next->delay);
+ status, count, delay, this, next, &dummy);
loranerror = 1;
}
+ ticker = this->scheduled;
+
write_eflags(ef);
}
@@ -569,7 +618,7 @@ static struct timecounter loran_timecounter = {
};
SYSCTL_OPAQUE(_debug, OID_AUTO, loran_timecounter, CTLFLAG_RD,
- loran_timecounter, sizeof(loran_timecounter), "S,timecounter", "");
+ &loran_timecounter, sizeof(loran_timecounter), "S,timecounter", "");
/**********************************************************************/
OpenPOWER on IntegriCloud