summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/tw.c
diff options
context:
space:
mode:
authorstark <stark@FreeBSD.org>1997-05-31 02:39:32 +0000
committerstark <stark@FreeBSD.org>1997-05-31 02:39:32 +0000
commita3186f5a8d3f335880383d2da6b9ca97cafb90fd (patch)
treecd50ebf27cb3eae8cdabb91066969cd572f170e9 /sys/i386/isa/tw.c
parent654c037a9cc5bd30071f3e268f19bffc09ed810a (diff)
downloadFreeBSD-src-a3186f5a8d3f335880383d2da6b9ca97cafb90fd.zip
FreeBSD-src-a3186f5a8d3f335880383d2da6b9ca97cafb90fd.tar.gz
Submitted by: Rich Murphey (ages ago) and Gene Stark
Hopefully I've done the proper magic to merge changes between 1.17 and 1.17.2.1 into the main trunk. Description of those changes follows: Brought in changes sent to me in late 1995 by Rich Murphey. I cleaned up a few things and am currently running these under 2.2-970205-GAMMA. The changes deal with software debouncing apparently necessary on todays faster hardware, and also some problems with the use of the -Select line for the TW-523 sync. This driver allows use of +PaperEnd as an alternative.
Diffstat (limited to 'sys/i386/isa/tw.c')
-rw-r--r--sys/i386/isa/tw.c238
1 files changed, 182 insertions, 56 deletions
diff --git a/sys/i386/isa/tw.c b/sys/i386/isa/tw.c
index a30d5b4..5b05ef7 100644
--- a/sys/i386/isa/tw.c
+++ b/sys/i386/isa/tw.c
@@ -108,7 +108,17 @@
* Transmit TX 4 (Y) 2, 4, 6, 8 Data out
* Receive RX 3 (G) 10, 14 -ACK, -AutoFeed
* Common 2 (R) 25 Common
- * Zero crossing 1 (B) 17 -Select Input
+ * Zero crossing 1 (B) 17 or 12 -Select or +PaperEnd
+ *
+ * NOTE: In the original cable I have (which I am still using, May, 1997)
+ * the Zero crossing signal goes to pin 17 (-Select) on the parallel port.
+ * In retrospect, this doesn't make a whole lot of sense, given that the
+ * -Select signal propagates the other direction. Indeed, some people have
+ * reported problems with this, and have had success using pin 12 (+PaperEnd)
+ * instead. This driver searches for the zero crossing signal on either
+ * pin 17 or pin 12, so it should work with either cable configuration.
+ * My suggestion would be to start by making the cable so that the zero
+ * crossing signal goes to pin 12 on the parallel port.
*
* The zero crossing signal is used to synchronize transmission to the
* zero crossings of the AC line, as detailed in the X-10 documentation.
@@ -149,8 +159,6 @@
#include <i386/isa/isa_device.h>
-
-
/*
* Transmission is done by calling write() to send three byte packets of data.
* The first byte contains a four bit house code (0=A to 15=P).
@@ -184,6 +192,7 @@
#define tw_status 1 /* Status of tw523 (R) */
#define TWS_RDATA 0x40 /* tw523 receive data */
+#define TWS_OUT 0x20 /* pin 12, out of paper */
#define tw_control 2 /* Control tw523 (R/W) */
#define TWC_SYNC 0x08 /* tw523 sync (pin 17) */
@@ -215,6 +224,7 @@ static struct cdevsw tw_cdevsw =
{ twopen, twclose, twread, twwrite, /*19*/
noioc, nullstop, nullreset, nodevtotty, /* tw */
twselect, nommap, nostrat, "tw", NULL, -1 };
+
/*
* Software control structure for TW523
*/
@@ -225,6 +235,7 @@ static struct cdevsw tw_cdevsw =
#define TWS_OPEN 8 /* Is it currently open? */
#define TW_SIZE 3*60 /* Enough for about 10 sec. of input */
+#define TW_MIN_DELAY 1500 /* Ignore interrupts of lesser latency */
static struct tw_sc {
u_int sc_port; /* I/O Port */
@@ -243,22 +254,29 @@ static struct tw_sc {
#ifdef HIRESTIME
int sc_xtimes[22]; /* Times for bits in current xmit packet */
int sc_rtimes[22]; /* Times for bits in current rcv packet */
+ int sc_no_rcv; /* number of interrupts received */
+#define SC_RCV_TIME_LEN 128
+ int sc_rcv_time[SC_RCV_TIME_LEN]; /* usec time stamp on interrupt */
#endif /* HIRESTIME */
#ifdef DEVFS
void *devfs_token; /* store the devfs handle */
#endif
} tw_sc[NTW];
+static int tw_zcport; /* offset of port for zero crossing signal */
+static int tw_zcmask; /* mask for the zero crossing signal */
+
static void twdelay25(void);
static void twdelayn(int n);
static void twsetuptimes(int *a);
static int wait_for_zero(struct tw_sc *sc);
+static int twputpkt(struct tw_sc *sc, u_char *p);
static int twgetbytes(struct tw_sc *sc, u_char *p, int cnt);
static timeout_t twabortrcv;
static int twsend(struct tw_sc *sc, int h, int k, int cnt);
static int next_zero(struct tw_sc *sc);
-static int twputpkt(struct tw_sc *sc, u_char *p);
static int twchecktime(int target, int tol);
+static void twdebugtimes(struct tw_sc *sc);
/*
* Counter value for delay loop.
@@ -279,7 +297,7 @@ static int twdelaycount;
* fairly forgiving.
*/
-static void twdelay25()
+static void twdelay25(void)
{
int cnt;
for(cnt = twdelaycount; cnt; cnt--); /* Should take about 25us */
@@ -314,8 +332,7 @@ static void twdelayn(int n)
}
}
-static int
-twprobe(idp)
+static int twprobe(idp)
struct isa_device *idp;
{
struct tw_sc sc;
@@ -323,6 +340,17 @@ twprobe(idp)
int tries;
sc.sc_port = idp->id_iobase;
+ /* Search for the zero crossing signal at ports, bit combinations. */
+ tw_zcport = tw_control;
+ tw_zcmask = TWC_SYNC;
+ sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask;
+ if(wait_for_zero(&sc) < 0) {
+ tw_zcport = tw_status;
+ tw_zcmask = TWS_OUT;
+ sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask;
+ }
+ if(wait_for_zero(&sc) < 0)
+ return(0);
/*
* Iteratively check the timing of a few sync transitions, and adjust
* the loop delay counter, if necessary, to bring the timing reported
@@ -332,7 +360,7 @@ twprobe(idp)
if(twdelaycount == 0) { /* Only adjust timing for first unit */
twdelaycount = TWDELAYCOUNT;
for(tries = 0; tries < 10; tries++) {
- sc.sc_xphase = inb(idp->id_iobase + tw_control) & TWC_SYNC;
+ sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask;
if(wait_for_zero(&sc) >= 0) {
d = wait_for_zero(&sc);
if(d <= HALFCYCLE/100 || d >= HALFCYCLE*100) {
@@ -346,16 +374,15 @@ twprobe(idp)
/*
* Now do a final check, just to make sure
*/
- sc.sc_xphase = inb(idp->id_iobase + tw_control) & TWC_SYNC;
+ sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask;
if(wait_for_zero(&sc) >= 0) {
d = wait_for_zero(&sc);
- if(d <= (HALFCYCLE * 110)/100 && d >= (HALFCYCLE * 90)/100) return(1);
+ if(d <= (HALFCYCLE * 110)/100 && d >= (HALFCYCLE * 90)/100) return(8);
}
return(0);
}
-static int
-twattach(idp)
+static int twattach(idp)
struct isa_device *idp;
{
struct tw_sc *sc;
@@ -364,6 +391,7 @@ twattach(idp)
sc = &tw_sc[unit = idp->id_unit];
sc->sc_port = idp->id_iobase;
sc->sc_state = 0;
+ sc->sc_rcount = 0;
#ifdef DEVFS
sc->devfs_token =
@@ -382,6 +410,7 @@ int twopen(dev, flag, mode, p)
{
struct tw_sc *sc = &tw_sc[TWUNIT(dev)];
int s;
+ int port;
s = spltty();
if(sc->sc_state == 0) {
@@ -402,6 +431,7 @@ int twclose(dev, flag, mode, p)
{
struct tw_sc *sc = &tw_sc[TWUNIT(dev)];
int s;
+ int port = sc->sc_port;
s = spltty();
sc->sc_state = 0;
@@ -504,7 +534,8 @@ int twselect(dev, rw, p)
struct proc *p;
{
struct tw_sc *sc;
- int s;
+ struct proc *pp;
+ int s, i;
sc = &tw_sc[TWUNIT(dev)];
s = spltty();
@@ -592,27 +623,60 @@ static char X10_KEY[32][10] = {
*/
static short X10_HOUSE_INV[16] = {
- 12, 4, 2, 10, 14, 6, 0, 8,
- 13, 5, 3, 11, 15, 7, 1, 9
+ 12, 4, 2, 10, 14, 6, 0, 8,
+ 13, 5, 3, 11, 15, 7, 1, 9
};
-static short X10_KEY_INV[32] = {
- 12, 16, 4, 17, 2, 18, 10, 19,
- 14, 20, 6, 21, 0, 22, 8, 23,
- 13, 24, 5, 25, 3, 26, 11, 27,
- 15, 28, 7, 29, 1, 30, 9, 31
+static short X10_KEY_INV[32] = {
+ 12, 16, 4, 17, 2, 18, 10, 19,
+ 14, 20, 6, 21, 0, 22, 8, 23,
+ 13, 24, 5, 25, 3, 26, 11, 27,
+ 15, 28, 7, 29, 1, 30, 9, 31
};
+static char *X10_KEY_LABEL[32] = {
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ "16",
+ "All Units Off",
+ "All Units On",
+ "On",
+ "Off",
+ "Dim",
+ "Bright",
+ "All LIGHTS Off",
+ "Extended Code",
+ "Hail Request",
+ "Hail Acknowledge",
+ "Preset Dim 0",
+ "Preset Dim 1",
+ "Extended Data (analog)",
+ "Status = on",
+ "Status = off",
+ "Status request"
+};
/*
* Transmit a packet containing house code h and key code k
*/
#define TWRETRY 10 /* Try 10 times to sync with AC line */
-static int
-twsend(sc, h, k, cnt)
- struct tw_sc *sc;
- int h, k, cnt;
+static int twsend(sc, h, k, cnt)
+struct tw_sc *sc;
+int h, k, cnt;
{
int i;
int port = sc->sc_port;
@@ -700,13 +764,13 @@ static int wait_for_zero(sc)
struct tw_sc *sc;
{
int i, old, new, max;
- int port = sc->sc_port + tw_control;
+ int port = sc->sc_port + tw_zcport;
old = sc->sc_xphase;
max = 10000; /* 10000 * 25us = 0.25 sec */
i = 0;
while(max--) {
- new = inb(port) & TWC_SYNC;
+ new = inb(port) & tw_zcmask;
if(new != old) {
sc->sc_xphase = new;
return(i*25);
@@ -814,15 +878,31 @@ twabortrcv(arg)
s = spltty();
sc->sc_state &= ~TWS_RCVING;
- sc->sc_flags |= TW_RCV_ERROR;
- pkt[0] = sc->sc_flags;
- pkt[1] = pkt[2] = 0;
- twputpkt(sc, pkt);
- log(LOG_ERR, "TWRCV: aborting (%x, %d)\n", sc->sc_bits, sc->sc_rcount);
+ /* simply ignore single isolated interrupts. */
+ if (sc->sc_no_rcv > 1) {
+ sc->sc_flags |= TW_RCV_ERROR;
+ pkt[0] = sc->sc_flags;
+ pkt[1] = pkt[2] = 0;
+ twputpkt(sc, pkt);
+ log(LOG_ERR, "TWRCV: aborting (%x, %d)\n", sc->sc_bits, sc->sc_rcount);
+ twdebugtimes(sc);
+ }
wakeup((caddr_t)sc);
splx(s);
}
+static int
+tw_is_within(int value, int expected, int tolerance)
+{
+ int diff;
+ diff = value - expected;
+ if (diff < 0)
+ diff *= -1;
+ if (diff < tolerance)
+ return 1;
+ return 0;
+}
+
/*
* This routine handles interrupts that occur when there is a falling
* transition on the RX input. There isn't going to be a transition
@@ -839,6 +919,8 @@ int unit;
int port;
int newphase;
u_char pkt[3];
+ int delay = 0;
+ struct timeval tv;
port = sc->sc_port;
/*
@@ -846,6 +928,8 @@ int unit;
*/
if(sc->sc_state == 0) return;
newphase = inb(port + tw_control) & TWC_SYNC;
+ microtime(&tv);
+
/*
* NEW PACKET:
* If we aren't currently receiving a packet, set up a new packet
@@ -862,20 +946,40 @@ int unit;
else sc->sc_flags = 0;
sc->sc_bits = 0;
sc->sc_rphase = newphase;
- timeout(twabortrcv, (caddr_t)sc, hz/4);
+ /* 3 cycles of silence = 3/60 = 1/20 = 50 msec */
+ timeout(twabortrcv, (caddr_t)sc, hz/20);
+ sc->sc_rcv_time[0] = tv.tv_usec;
+ sc->sc_no_rcv = 1;
return;
}
+ untimeout((timeout_func_t)twabortrcv, (caddr_t)sc);
+ timeout((timeout_func_t)twabortrcv, (caddr_t)sc, hz/20);
+ newphase = inb(port + tw_zcport) & tw_zcmask;
+
+ /* enforce a minimum delay since the last interrupt */
+ delay = tv.tv_usec - sc->sc_rcv_time[sc->sc_no_rcv - 1];
+ if (delay < 0)
+ delay += 1000000;
+ if (delay < TW_MIN_DELAY)
+ return;
+
+ sc->sc_rcv_time[sc->sc_no_rcv] = tv.tv_usec;
+ if (sc->sc_rcv_time[sc->sc_no_rcv] < sc->sc_rcv_time[0])
+ sc->sc_rcv_time[sc->sc_no_rcv] += 1000000;
+ sc->sc_no_rcv++;
+
/*
* START CODE:
* The second and third bits are a special case.
*/
- if(sc->sc_rcount < 3) {
+ if (sc->sc_rcount < 3) {
+ if (
#ifdef HIRESTIME
- if(twchecktime(sc->sc_rtimes[sc->sc_rcount], HALFCYCLE/3)
- && newphase != sc->sc_rphase) {
+ tw_is_within(delay, HALFCYCLE, HALFCYCLE / 6)
#else
- if(newphase != sc->sc_rphase) {
+ newphase != sc->sc_rphase
#endif
+ ) {
sc->sc_rcount++;
} else {
/*
@@ -883,14 +987,10 @@ int unit;
*/
sc->sc_state &= ~TWS_RCVING;
sc->sc_flags |= TW_RCV_ERROR;
-/*
- pkt[0] = sc->sc_flags;
- pkt[1] = pkt[2] = 0;
- twputpkt(sc, pkt);
- wakeup((caddr_t)sc);
- */
untimeout(twabortrcv, (caddr_t)sc);
log(LOG_ERR, "TWRCV: Invalid start code\n");
+ twdebugtimes(sc);
+ sc->sc_no_rcv = 0;
return;
}
if(sc->sc_rcount == 3) {
@@ -936,19 +1036,32 @@ int unit;
*/
if(sc->sc_rcount <= 20) {
#ifdef HIRESTIME
- if((newphase == sc->sc_rphase &&
- twchecktime(sc->sc_rtimes[sc->sc_rcount+1], HALFCYCLE/3) == 0)
- || (newphase != sc->sc_rphase &&
- twchecktime(sc->sc_rtimes[sc->sc_rcount], HALFCYCLE/3) == 0)) {
+ int bit = 0, last_bit;
+ if (sc->sc_rcount == 4)
+ last_bit = 1; /* Start (1110) ends in 10, a 'one' code. */
+ else
+ last_bit = sc->sc_bits & 0x1;
+ if ( ( (last_bit == 1)
+ && (tw_is_within(delay, HALFCYCLE * 2, HALFCYCLE / 6)))
+ || ( (last_bit == 0)
+ && (tw_is_within(delay, HALFCYCLE * 1, HALFCYCLE / 6))))
+ bit = 1;
+ else if ( ( (last_bit == 1)
+ && (tw_is_within(delay, HALFCYCLE * 3, HALFCYCLE / 6)))
+ || ( (last_bit == 0)
+ && (tw_is_within(delay, HALFCYCLE * 2, HALFCYCLE / 6))))
+ bit = 0;
+ else {
sc->sc_flags |= TW_RCV_ERROR;
- } else {
-#endif /* HIRESTIME */
- sc->sc_bits = (sc->sc_bits << 1)
- | ((newphase == sc->sc_rphase) ? 0x0 : 0x1);
- sc->sc_rcount += 2;
-#ifdef HIRESTIME
+ log(LOG_ERR, "TWRCV: %d cycle after %d bit, delay %d%%\n",
+ sc->sc_rcount, last_bit, 100 * delay / HALFCYCLE);
}
+ sc->sc_bits = (sc->sc_bits << 1) | bit;
+#else
+ sc->sc_bits = (sc->sc_bits << 1)
+ | ((newphase == sc->sc_rphase) ? 0x0 : 0x1);
#endif /* HIRESTIME */
+ sc->sc_rcount += 2;
}
if(sc->sc_rcount >= 22 || sc->sc_flags & TW_RCV_ERROR) {
if(sc->sc_rcount != 22) {
@@ -963,13 +1076,27 @@ int unit;
sc->sc_state &= ~TWS_RCVING;
twputpkt(sc, pkt);
untimeout(twabortrcv, (caddr_t)sc);
- if(sc->sc_flags & TW_RCV_ERROR)
- log(LOG_ERR, "TWRCV: invalid packet: (%d, %x)\n",
- sc->sc_rcount, sc->sc_bits);
+ if(sc->sc_flags & TW_RCV_ERROR) {
+ log(LOG_ERR, "TWRCV: invalid packet: (%d, %x) %c %d\n",
+ sc->sc_rcount, sc->sc_bits, 'A' + pkt[1], X10_KEY_LABEL[pkt[2]]);
+ twdebugtimes(sc);
+ } else {
+/* log(LOG_ERR, "TWRCV: valid packet: (%d, %x) %c %s\n",
+ sc->sc_rcount, sc->sc_bits, 'A' + pkt[1], X10_KEY_LABEL[pkt[2]]); */
+ }
+ sc->sc_rcount = 0;
wakeup((caddr_t)sc);
}
}
+static void twdebugtimes(struct tw_sc *sc)
+{
+ int i;
+ for (i = 0; (i < sc->sc_no_rcv) && (i < SC_RCV_TIME_LEN); i++)
+ log(LOG_ERR, "TWRCV: interrupt %2d: %d\t%d%%\n", i, sc->sc_rcv_time[i],
+ (sc->sc_rcv_time[i] - sc->sc_rcv_time[(i?i-1:0)])*100/HALFCYCLE);
+}
+
#ifdef HIRESTIME
/*
* Initialize an array of 22 times, starting from the current
@@ -1010,7 +1137,6 @@ static int twchecktime(int target, int tol)
if(d <= tol && d >= -tol) {
return(1);
} else {
- log(LOG_ERR, "TWCHK: timing off by %dus (>= %dus)\n", d, tol);
return(0);
}
}
OpenPOWER on IntegriCloud