diff options
author | stark <stark@FreeBSD.org> | 1997-05-31 02:39:32 +0000 |
---|---|---|
committer | stark <stark@FreeBSD.org> | 1997-05-31 02:39:32 +0000 |
commit | a3186f5a8d3f335880383d2da6b9ca97cafb90fd (patch) | |
tree | cd50ebf27cb3eae8cdabb91066969cd572f170e9 /sys/i386/isa/tw.c | |
parent | 654c037a9cc5bd30071f3e268f19bffc09ed810a (diff) | |
download | FreeBSD-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.c | 238 |
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); } } |