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.
This commit is contained in:
parent
bc3718bb36
commit
bb7e0661df
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue