From 5899148189662ad99f58caaf963b1838a2ab6965 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 27 Apr 2003 05:30:52 +0000 Subject: [PATCH] After careful review of this driver, I'm pretty sure it would take a lot of work to make this driver work under current. In the past when people wanted to remove xten, I was the only one in the way. After talking to fsmp@ (last person to make real changes to this driver) about this, I'm convinced it is better left in the dust-bin of history Approved by: re@ (scottl) --- sys/i386/isa/tw.c | 1157 --------------------------------------------- 1 file changed, 1157 deletions(-) delete mode 100644 sys/i386/isa/tw.c diff --git a/sys/i386/isa/tw.c b/sys/i386/isa/tw.c deleted file mode 100644 index d0d0e65f8f6c..000000000000 --- a/sys/i386/isa/tw.c +++ /dev/null @@ -1,1157 +0,0 @@ -/*- - * Copyright (c) 1992, 1993, 1995 Eugene W. Stark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Eugene W. Stark. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - * - */ - -#include "tw.h" - -/* - * Driver configuration parameters - */ - -/* - * Time for 1/2 of a power line cycle, in microseconds. - * Change this to 10000 for 50Hz power. Phil Sampson - * (vk2jnt@gw.vk2jnt.ampr.org OR sampson@gidday.enet.dec.com) - * reports that this works (at least in Australia) using a - * TW7223 module (a local version of the TW523). - */ -#define HALFCYCLE 8333 /* 1/2 cycle = 8333us at 60Hz */ - -/* - * Undefine the following if you don't have the high-resolution "microtime" - * routines (leave defined for FreeBSD, which has them). - */ -#define HIRESTIME - -/* - * End of driver configuration parameters - */ - -/* - * FreeBSD Device Driver for X-10 POWERHOUSE (tm) - * Two-Way Power Line Interface, Model #TW523 - * - * written by Eugene W. Stark (stark@cs.sunysb.edu) - * December 2, 1992 - * - * NOTES: - * - * The TW523 is a carrier-current modem for home control/automation purposes. - * It is made by: - * - * X-10 Inc. - * 185A LeGrand Ave. - * Northvale, NJ 07647 - * USA - * (201) 784-9700 or 1-800-526-0027 - * - * X-10 Home Controls Inc. - * 1200 Aerowood Drive, Unit 20 - * Mississauga, Ontario - * (416) 624-4446 or 1-800-387-3346 - * - * The TW523 is designed for communications using the X-10 protocol, - * which is compatible with a number of home control systems, including - * Radio Shack "Plug 'n Power(tm)" and Stanley "Lightmaker(tm)." - * I bought my TW523 from: - * - * Home Control Concepts - * 9353-C Activity Road - * San Diego, CA 92126 - * (619) 693-8887 - * - * They supplied me with the TW523 (which has an RJ-11 four-wire modular - * telephone connector), a modular cable, an RJ-11 to DB-25 connector with - * internal wiring, documentation from X-10 on the TW523 (very good), - * an instruction manual by Home Control Concepts (not very informative), - * and a floppy disk containing binary object code of some demonstration/test - * programs and of a C function library suitable for controlling the TW523 - * by an IBM PC under MS-DOS (not useful to me other than to verify that - * the unit worked). I suggest saving money and buying the bare TW523 - * rather than the TW523 development kit (what I bought), because if you - * are running FreeBSD you don't really care about the DOS binaries. - * - * The interface to the TW-523 consists of four wires on the RJ-11 connector, - * which are jumpered to somewhat more wires on the DB-25 connector, which - * in turn is intended to plug into the PC parallel printer port. I dismantled - * the DB-25 connector to find out what they had done: - * - * Signal RJ-11 pin DB-25 pin(s) Parallel Port - * 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 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. - * It would be nice if one could generate interrupts with this signal, - * however one needs interrupts on both the rising and falling edges, - * and the -ACK signal to the parallel port interrupts only on the falling - * edge, so it can't be done without additional hardware. - * - * In this driver, the transmit function is performed in a non-interrupt-driven - * fashion, by polling the zero crossing signal to determine when a transition - * has occurred. This wastes CPU time during transmission, but it seems like - * the best that can be done without additional hardware. One problem with - * the scheme is that preemption of the CPU during transmission can cause loss - * of sync. The driver tries to catch this, by noticing that a long delay - * loop has somehow become foreshortened, and the transmission is aborted with - * an error return. It is up to the user level software to handle this - * situation (most likely by retrying the transmission). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HIRESTIME -#include -#endif /* HIRESTIME */ - -#include - -#ifndef COMPAT_OLDISA -#error "The tw device requires the old isa compatibility shims" -#endif - -/* - * 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). - * The second byte contains five bit unit/key code (0=unit 1 to 15=unit 16, - * 16=All Units Off to 31 = Status Request). The third byte specifies - * the number of times the packet is to be transmitted without any - * gaps between successive transmissions. Normally this is 2, as per - * the X-10 documentation, but sometimes (e.g. for bright and dim codes) - * it can be another value. Each call to write can specify an arbitrary - * number of data bytes. An incomplete packet is buffered until a subsequent - * call to write() provides data to complete it. At most one packet will - * actually be processed in any call to write(). Successive calls to write() - * leave a three-cycle gap between transmissions, per the X-10 documentation. - * - * Reception is done using read(). - * The driver produces a series of three-character packets. - * In each packet, the first character consists of flags, - * the second character is a four bit house code (0-15), - * and the third character is a five bit key/function code (0-31). - * The flags are the following: - */ - -#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */ -#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */ - -/* - * IBM PC parallel port definitions relevant to TW523 - */ - -#define tw_data 0 /* Data to tw523 (R/W) */ - -#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) */ -#define TWC_ENA 0x10 /* tw523 interrupt enable */ - -/* - * Miscellaneous defines - */ - -#define TWUNIT(dev) (minor(dev)) /* Extract unit number from device */ -#define TWPRI (PZERO+8) /* I don't know any better, so let's */ - /* use the same as the line printer */ - -static int twprobe(struct isa_device *idp); -static int twattach(struct isa_device *idp); - -struct isa_driver twdriver = { - INTR_TYPE_TTY, - twprobe, - twattach, - "tw" -}; -COMPAT_ISA_DRIVER(tw, twdriver); - -static d_open_t twopen; -static d_close_t twclose; -static d_read_t twread; -static d_write_t twwrite; -static d_poll_t twpoll; - -#define CDEV_MAJOR 19 -static struct cdevsw tw_cdevsw = { - .d_open = twopen, - .d_close = twclose, - .d_read = twread, - .d_write = twwrite, - .d_poll = twpoll, - .d_name = "tw", - .d_maj = CDEV_MAJOR, -}; - -/* - * Software control structure for TW523 - */ - -#define TWS_XMITTING 1 /* Transmission in progress */ -#define TWS_RCVING 2 /* Reception in progress */ -#define TWS_WANT 4 /* A process wants received data */ -#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 */ - u_int sc_state; /* Current software control state */ - struct selinfo sc_selp; /* Information for select() */ - u_char sc_xphase; /* Current state of sync (for transmitter) */ - u_char sc_rphase; /* Current state of sync (for receiver) */ - u_char sc_flags; /* Flags for current reception */ - short sc_rcount; /* Number of bits received so far */ - int sc_bits; /* Bits received so far */ - u_char sc_pkt[3]; /* Packet not yet transmitted */ - short sc_pktsize; /* How many bytes in the packet? */ - u_char sc_buf[TW_SIZE]; /* We buffer our own input */ - int sc_nextin; /* Next free slot in circular buffer */ - int sc_nextout; /* First used slot in circular buffer */ - /* Callout for canceling our abortrcv timeout */ - struct callout_handle abortrcv_ch; -#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 */ -} 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 ointhand2_t twintr; -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 twchecktime(int target, int tol); -static void twdebugtimes(struct tw_sc *sc); - -/* - * Counter value for delay loop. - * It is adjusted by twprobe so that the delay loop takes about 25us. - */ - -#define TWDELAYCOUNT 161 /* Works on my 486DX/33 */ -static int twdelaycount; - -/* - * Twdelay25 is used for very short delays of about 25us. - * It is implemented with a calibrated delay loop, and should be - * fairly accurate ... unless we are preempted by an interrupt. - * - * We use this to wait for zero crossings because the X-10 specs say we - * are supposed to assert carrier within 25us when one happens. - * I don't really believe we can do this, but the X-10 devices seem to be - * fairly forgiving. - */ - -static void twdelay25(void) -{ - int cnt; - for(cnt = twdelaycount; cnt; cnt--); /* Should take about 25us */ -} - -/* - * Twdelayn is used to time the length of the 1ms carrier pulse. - * This is not very critical, but if we have high-resolution time-of-day - * we check it every apparent 200us to make sure we don't get too far off - * if we happen to be interrupted during the delay. - */ - -static void twdelayn(int n) -{ -#ifdef HIRESTIME - int t, d; - struct timeval tv; - microtime(&tv); - t = tv.tv_usec; - t += n; -#endif /* HIRESTIME */ - while(n > 0) { - twdelay25(); - n -= 25; -#ifdef HIRESTIME - if((n & 0x7) == 0) { - microtime(&tv); - d = tv.tv_usec - t; - if(d >= 0 && d < 1000000) return; - } -#endif /* HIRESTIME */ - } -} - -static int twprobe(idp) - struct isa_device *idp; -{ - struct tw_sc sc; - int d; - 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 - * by wait_for_zero() close to HALFCYCLE. Give up if anything - * ridiculous happens. - */ - 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_zcport) & tw_zcmask; - if(wait_for_zero(&sc) >= 0) { - d = wait_for_zero(&sc); - if(d <= HALFCYCLE/100 || d >= HALFCYCLE*100) { - twdelaycount = 0; - return(0); - } - twdelaycount = (twdelaycount * d)/HALFCYCLE; - } - } - } - /* - * Now do a final check, just to make sure - */ - 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(8); - } - return(0); -} - -static int twattach(idp) - struct isa_device *idp; -{ - struct tw_sc *sc; - int unit; - - idp->id_ointr = twintr; - sc = &tw_sc[unit = idp->id_unit]; - sc->sc_port = idp->id_iobase; - sc->sc_state = 0; - sc->sc_rcount = 0; - callout_handle_init(&sc->abortrcv_ch); - make_dev(&tw_cdevsw, unit, 0, 0, 0600, "tw%d", unit); - return (1); -} - -static int -twopen(dev, flag, mode, td) - dev_t dev; - int flag; - int mode; - struct thread *td; -{ - struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; - int s; - - s = spltty(); - if(sc->sc_state == 0) { - sc->sc_state = TWS_OPEN; - sc->sc_nextin = sc->sc_nextout = 0; - sc->sc_pktsize = 0; - outb(sc->sc_port+tw_control, TWC_ENA); - } - splx(s); - return(0); -} - -static int -twclose(dev, flag, mode, td) - dev_t dev; - int flag; - int mode; - struct thread *td; -{ - struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; - int s; - - s = spltty(); - sc->sc_state = 0; - outb(sc->sc_port+tw_control, 0); - splx(s); - return(0); -} - -static int -twread(dev, uio, ioflag) - dev_t dev; - struct uio *uio; - int ioflag; -{ - u_char buf[3]; - struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; - int error, cnt, s; - - s = spltty(); - cnt = MIN(uio->uio_resid, 3); - if((error = twgetbytes(sc, buf, cnt)) == 0) { - error = uiomove(buf, cnt, uio); - } - splx(s); - return(error); -} - -static int -twwrite(dev, uio, ioflag) - dev_t dev; - struct uio *uio; - int ioflag; -{ - struct tw_sc *sc; - int house, key, reps; - int s, error; - int cnt; - - sc = &tw_sc[TWUNIT(dev)]; - /* - * Note: Although I had intended to allow concurrent transmitters, - * there is a potential problem here if two processes both write - * into the sc_pkt buffer at the same time. The following code - * is an additional critical section that needs to be synchronized. - */ - s = spltty(); - cnt = MIN(3 - sc->sc_pktsize, uio->uio_resid); - error = uiomove(&(sc->sc_pkt[sc->sc_pktsize]), cnt, uio); - if(error) { - splx(s); - return(error); - } - sc->sc_pktsize += cnt; - if(sc->sc_pktsize < 3) { /* Only transmit 3-byte packets */ - splx(s); - return(0); - } - sc->sc_pktsize = 0; - /* - * Collect house code, key code, and rep count, and check for sanity. - */ - house = sc->sc_pkt[0]; - key = sc->sc_pkt[1]; - reps = sc->sc_pkt[2]; - if(house >= 16 || key >= 32) { - splx(s); - return(ENODEV); - } - /* - * Synchronize with the receiver operating in the bottom half, and - * also with concurrent transmitters. - * We don't want to interfere with a packet currently being received, - * and we would like the receiver to recognize when a packet has - * originated locally. - */ - while(sc->sc_state & (TWS_RCVING | TWS_XMITTING)) { - error = tsleep(sc, TWPRI|PCATCH, "twwrite", 0); - if(error) { - splx(s); - return(error); - } - } - sc->sc_state |= TWS_XMITTING; - /* - * Everything looks OK, let's do the transmission. - */ - splx(s); /* Enable interrupts because this takes a LONG time */ - error = twsend(sc, house, key, reps); - s = spltty(); - sc->sc_state &= ~TWS_XMITTING; - wakeup(sc); - splx(s); - if(error) return(EIO); - else return(0); -} - -/* - * Determine if there is data available for reading - */ - -static int -twpoll(dev, events, td) - dev_t dev; - int events; - struct thread *td; -{ - struct tw_sc *sc; - int s; - int revents = 0; - - sc = &tw_sc[TWUNIT(dev)]; - s = spltty(); - /* XXX is this correct? the original code didn't test select rw mode!! */ - if (events & (POLLIN | POLLRDNORM)) { - if(sc->sc_nextin != sc->sc_nextout) - revents |= events & (POLLIN | POLLRDNORM); - else - selrecord(td, &sc->sc_selp); - } - splx(s); - return(revents); -} - -/* - * X-10 Protocol - */ - -#define X10_START_LENGTH 4 -static char X10_START[] = { 1, 1, 1, 0 }; - -/* - * Each bit of the 4-bit house code and 5-bit key code - * is transmitted twice, once in true form, and then in - * complemented form. This is already taken into account - * in the following tables. - */ - -#define X10_HOUSE_LENGTH 8 -static char X10_HOUSE[16][8] = { - { 0, 1, 1, 0, 1, 0, 0, 1 }, /* A = 0110 */ - { 1, 0, 1, 0, 1, 0, 0, 1 }, /* B = 1110 */ - { 0, 1, 0, 1, 1, 0, 0, 1 }, /* C = 0010 */ - { 1, 0, 0, 1, 1, 0, 0, 1 }, /* D = 1010 */ - { 0, 1, 0, 1, 0, 1, 1, 0 }, /* E = 0001 */ - { 1, 0, 0, 1, 0, 1, 1, 0 }, /* F = 1001 */ - { 0, 1, 1, 0, 0, 1, 1, 0 }, /* G = 0101 */ - { 1, 0, 1, 0, 0, 1, 1, 0 }, /* H = 1101 */ - { 0, 1, 1, 0, 1, 0, 1, 0 }, /* I = 0111 */ - { 1, 0, 1, 0, 1, 0, 1, 0 }, /* J = 1111 */ - { 0, 1, 0, 1, 1, 0, 1, 0 }, /* K = 0011 */ - { 1, 0, 0, 1, 1, 0, 1, 0 }, /* L = 1011 */ - { 0, 1, 0, 1, 0, 1, 0, 1 }, /* M = 0000 */ - { 1, 0, 0, 1, 0, 1, 0, 1 }, /* N = 1000 */ - { 0, 1, 1, 0, 0, 1, 0, 1 }, /* O = 0100 */ - { 1, 0, 1, 0, 0, 1, 0, 1 } /* P = 1100 */ -}; - -#define X10_KEY_LENGTH 10 -static char X10_KEY[32][10] = { - { 0, 1, 1, 0, 1, 0, 0, 1, 0, 1 }, /* 01100 => 1 */ - { 1, 0, 1, 0, 1, 0, 0, 1, 0, 1 }, /* 11100 => 2 */ - { 0, 1, 0, 1, 1, 0, 0, 1, 0, 1 }, /* 00100 => 3 */ - { 1, 0, 0, 1, 1, 0, 0, 1, 0, 1 }, /* 10100 => 4 */ - { 0, 1, 0, 1, 0, 1, 1, 0, 0, 1 }, /* 00010 => 5 */ - { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1 }, /* 10010 => 6 */ - { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1 }, /* 01010 => 7 */ - { 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 }, /* 11010 => 8 */ - { 0, 1, 1, 0, 1, 0, 1, 0, 0, 1 }, /* 01110 => 9 */ - { 1, 0, 1, 0, 1, 0, 1, 0, 0, 1 }, /* 11110 => 10 */ - { 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 }, /* 00110 => 11 */ - { 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 }, /* 10110 => 12 */ - { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, /* 00000 => 13 */ - { 1, 0, 0, 1, 0, 1, 0, 1, 0, 1 }, /* 10000 => 14 */ - { 0, 1, 1, 0, 0, 1, 0, 1, 0, 1 }, /* 01000 => 15 */ - { 1, 0, 1, 0, 0, 1, 0, 1, 0, 1 }, /* 11000 => 16 */ - { 0, 1, 0, 1, 0, 1, 0, 1, 1, 0 }, /* 00001 => All Units Off */ - { 0, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, /* 00011 => All Units On */ - { 0, 1, 0, 1, 1, 0, 0, 1, 1, 0 }, /* 00101 => On */ - { 0, 1, 0, 1, 1, 0, 1, 0, 1, 0 }, /* 00111 => Off */ - { 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }, /* 01001 => Dim */ - { 0, 1, 1, 0, 0, 1, 1, 0, 1, 0 }, /* 01011 => Bright */ - { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0 }, /* 01101 => All LIGHTS Off */ - { 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 }, /* 01111 => Extended Code */ - { 1, 0, 0, 1, 0, 1, 0, 1, 1, 0 }, /* 10001 => Hail Request */ - { 1, 0, 0, 1, 0, 1, 1, 0, 1, 0 }, /* 10011 => Hail Acknowledge */ - { 1, 0, 0, 1, 1, 0, 0, 1, 1, 0 }, /* 10101 => Preset Dim 0 */ - { 1, 0, 0, 1, 1, 0, 1, 0, 1, 0 }, /* 10111 => Preset Dim 1 */ - { 1, 0, 1, 0, 0, 1, 0, 1, 0, 1 }, /* 11000 => Ext Data (analog) */ - { 1, 0, 1, 0, 0, 1, 1, 0, 1, 0 }, /* 11011 => Status = on */ - { 1, 0, 1, 0, 1, 0, 0, 1, 1, 0 }, /* 11101 => Status = off */ - { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 } /* 11111 => Status request */ -}; - -/* - * Tables for mapping received X-10 code back to house/key number. - */ - -static short X10_HOUSE_INV[16] = { - 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 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; -{ - int i; - int port = sc->sc_port; - - /* - * Make sure we get a reliable sync with a power line zero crossing - */ - for(i = 0; i < TWRETRY; i++) { - if(wait_for_zero(sc) > 100) goto insync; - } - log(LOG_ERR, "TWXMIT: failed to sync.\n"); - return(-1); - - insync: - /* - * Be sure to leave 3 cycles space between transmissions - */ - for(i = 6; i > 0; i--) - if(next_zero(sc) < 0) return(-1); - /* - * The packet is transmitted cnt times, with no gaps. - */ - while(cnt--) { - /* - * Transmit the start code - */ - for(i = 0; i < X10_START_LENGTH; i++) { - outb(port+tw_data, X10_START[i] ? 0xff : 0x00); /* Waste no time! */ -#ifdef HIRESTIME - if(i == 0) twsetuptimes(sc->sc_xtimes); - if(twchecktime(sc->sc_xtimes[i], HALFCYCLE/20) == 0) { - outb(port+tw_data, 0); - return(-1); - } -#endif /* HIRESTIME */ - twdelayn(1000); /* 1ms pulse width */ - outb(port+tw_data, 0); - if(next_zero(sc) < 0) return(-1); - } - /* - * Transmit the house code - */ - for(i = 0; i < X10_HOUSE_LENGTH; i++) { - outb(port+tw_data, X10_HOUSE[h][i] ? 0xff : 0x00); /* Waste no time! */ -#ifdef HIRESTIME - if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH], HALFCYCLE/20) == 0) { - outb(port+tw_data, 0); - return(-1); - } -#endif /* HIRESTIME */ - twdelayn(1000); /* 1ms pulse width */ - outb(port+tw_data, 0); - if(next_zero(sc) < 0) return(-1); - } - /* - * Transmit the unit/key code - */ - for(i = 0; i < X10_KEY_LENGTH; i++) { - outb(port+tw_data, X10_KEY[k][i] ? 0xff : 0x00); -#ifdef HIRESTIME - if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH+X10_HOUSE_LENGTH], - HALFCYCLE/20) == 0) { - outb(port+tw_data, 0); - return(-1); - } -#endif /* HIRESTIME */ - twdelayn(1000); /* 1ms pulse width */ - outb(port+tw_data, 0); - if(next_zero(sc) < 0) return(-1); - } - } - return(0); -} - -/* - * Waste CPU cycles to get in sync with a power line zero crossing. - * The value returned is roughly how many microseconds we wasted before - * seeing the transition. To avoid wasting time forever, we give up after - * waiting patiently for 1/4 sec (15 power line cycles at 60 Hz), - * which is more than the 11 cycles it takes to transmit a full - * X-10 packet. - */ - -static int wait_for_zero(sc) -struct tw_sc *sc; -{ - int i, old, new, max; - 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) & tw_zcmask; - if(new != old) { - sc->sc_xphase = new; - return(i*25); - } - i++; - twdelay25(); - } - return(-1); -} - -/* - * Wait for the next zero crossing transition, and if we don't have - * high-resolution time-of-day, check to see that the zero crossing - * appears to be arriving on schedule. - * We expect to be waiting almost a full half-cycle (8.333ms-1ms = 7.333ms). - * If we don't seem to wait very long, something is wrong (like we got - * preempted!) and we should abort the transmission because - * there's no telling how long it's really been since the - * last bit was transmitted. - */ - -static int next_zero(sc) -struct tw_sc *sc; -{ - int d; -#ifdef HIRESTIME - if((d = wait_for_zero(sc)) < 0) { -#else - if((d = wait_for_zero(sc)) < 6000 || d > 8500) { - /* No less than 6.0ms, no more than 8.5ms */ -#endif /* HIRESTIME */ - log(LOG_ERR, "TWXMIT framing error: %d\n", d); - return(-1); - } - return(0); -} - -/* - * Put a three-byte packet into the circular buffer - * Should be called at priority spltty() - */ - -static int twputpkt(sc, p) -struct tw_sc *sc; -u_char *p; -{ - int i, next; - - for(i = 0; i < 3; i++) { - next = sc->sc_nextin+1; - if(next >= TW_SIZE) next = 0; - if(next == sc->sc_nextout) { /* Buffer full */ -/* - log(LOG_ERR, "TWRCV: Buffer overrun\n"); - */ - return(1); - } - sc->sc_buf[sc->sc_nextin] = *p++; - sc->sc_nextin = next; - } - if(sc->sc_state & TWS_WANT) { - sc->sc_state &= ~TWS_WANT; - wakeup((&sc->sc_buf)); - } - selwakeup(&sc->sc_selp); - return(0); -} - -/* - * Get bytes from the circular buffer - * Should be called at priority spltty() - */ - -static int twgetbytes(sc, p, cnt) -struct tw_sc *sc; -u_char *p; -int cnt; -{ - int error; - - while(cnt--) { - while(sc->sc_nextin == sc->sc_nextout) { /* Buffer empty */ - sc->sc_state |= TWS_WANT; - error = tsleep((&sc->sc_buf), TWPRI|PCATCH, "twread", 0); - if(error) { - return(error); - } - } - *p++ = sc->sc_buf[sc->sc_nextout++]; - if(sc->sc_nextout >= TW_SIZE) sc->sc_nextout = 0; - } - return(0); -} - -/* - * Abort reception that has failed to complete in the required time. - */ - -static void -twabortrcv(arg) - void *arg; -{ - struct tw_sc *sc = arg; - int s; - u_char pkt[3]; - - s = spltty(); - sc->sc_state &= ~TWS_RCVING; - /* 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(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 - * on every bit (some are zero), but if we are smart and keep track of - * how long it's been since the last interrupt (via the zero crossing - * detect line and/or high-resolution time-of-day routine), we can - * reconstruct the transmission without having to poll. - */ - -static void twintr(unit) -int unit; -{ - struct tw_sc *sc = &tw_sc[unit]; - int port; - int newphase; - u_char pkt[3]; - int delay = 0; - struct timeval tv; - - port = sc->sc_port; - /* - * Ignore any interrupts that occur if the device is not open. - */ - if(sc->sc_state == 0) return; - newphase = inb(port + tw_zcport) & tw_zcmask; - microtime(&tv); - - /* - * NEW PACKET: - * If we aren't currently receiving a packet, set up a new packet - * and put in the first "1" bit that has just arrived. - * Arrange for the reception to be aborted if too much time goes by. - */ - if((sc->sc_state & TWS_RCVING) == 0) { -#ifdef HIRESTIME - twsetuptimes(sc->sc_rtimes); -#endif /* HIRESTIME */ - sc->sc_state |= TWS_RCVING; - sc->sc_rcount = 1; - if(sc->sc_state & TWS_XMITTING) sc->sc_flags = TW_RCV_LOCAL; - else sc->sc_flags = 0; - sc->sc_bits = 0; - sc->sc_rphase = newphase; - /* 3 cycles of silence = 3/60 = 1/20 = 50 msec */ - sc->abortrcv_ch = timeout(twabortrcv, (caddr_t)sc, hz/20); - sc->sc_rcv_time[0] = tv.tv_usec; - sc->sc_no_rcv = 1; - return; - } - untimeout(twabortrcv, (caddr_t)sc, sc->abortrcv_ch); - sc->abortrcv_ch = timeout(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 ( -#ifdef HIRESTIME - tw_is_within(delay, HALFCYCLE, HALFCYCLE / 6) -#else - newphase != sc->sc_rphase -#endif - ) { - sc->sc_rcount++; - } else { - /* - * Invalid start code -- abort reception. - */ - sc->sc_state &= ~TWS_RCVING; - sc->sc_flags |= TW_RCV_ERROR; - untimeout(twabortrcv, (caddr_t)sc, sc->abortrcv_ch); - log(LOG_ERR, "TWRCV: Invalid start code\n"); - twdebugtimes(sc); - sc->sc_no_rcv = 0; - return; - } - if(sc->sc_rcount == 3) { - /* - * We've gotten three "1" bits in a row. The start code - * is really 1110, but this might be followed by a zero - * bit from the house code, so if we wait any longer we - * might be confused about the first house code bit. - * So, we guess that the start code is correct and insert - * the trailing zero without actually having seen it. - * We don't change sc_rphase in this case, because two - * bit arrivals in a row preserve parity. - */ - sc->sc_rcount++; - return; - } - /* - * Update sc_rphase to the current phase before returning. - */ - sc->sc_rphase = newphase; - return; - } - /* - * GENERAL CASE: - * Now figure out what the current bit is that just arrived. - * The X-10 protocol transmits each data bit twice: once in - * true form and once in complemented form on the next half - * cycle. So, there will be at least one interrupt per bit. - * By comparing the phase we see at the time of the interrupt - * with the saved sc_rphase, we can tell on which half cycle - * the interrupt occrred. This assumes, of course, that the - * packet is well-formed. We do the best we can at trying to - * catch errors by aborting if too much time has gone by, and - * by tossing out a packet if too many bits arrive, but the - * whole scheme is probably not as robust as if we had a nice - * interrupt on every half cycle of the power line. - * If we have high-resolution time-of-day routines, then we - * can do a bit more sanity checking. - */ - - /* - * A complete packet is 22 half cycles. - */ - if(sc->sc_rcount <= 20) { -#ifdef HIRESTIME - 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; - 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) { - sc->sc_flags |= TW_RCV_ERROR; - pkt[0] = sc->sc_flags; - pkt[1] = pkt[2] = 0; - } else { - pkt[0] = sc->sc_flags; - pkt[1] = X10_HOUSE_INV[(sc->sc_bits & 0x1e0) >> 5]; - pkt[2] = X10_KEY_INV[sc->sc_bits & 0x1f]; - } - sc->sc_state &= ~TWS_RCVING; - twputpkt(sc, pkt); - untimeout(twabortrcv, (caddr_t)sc, sc->abortrcv_ch); - if(sc->sc_flags & TW_RCV_ERROR) { - log(LOG_ERR, "TWRCV: invalid packet: (%d, %x) %c %s\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(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 - * microtime and continuing for the next 21 half cycles. - * We use the times as a reference to make sure transmission - * or reception is on schedule. - */ - -static void twsetuptimes(int *a) -{ - struct timeval tv; - int i, t; - - microtime(&tv); - t = tv.tv_usec; - for(i = 0; i < 22; i++) { - *a++ = t; - t += HALFCYCLE; - if(t >= 1000000) t -= 1000000; - } -} - -/* - * Check the current time against a slot in a previously set up - * timing array, and make sure that it looks like we are still - * on schedule. - */ - -static int twchecktime(int target, int tol) -{ - struct timeval tv; - int t, d; - - microtime(&tv); - t = tv.tv_usec; - d = (target - t) >= 0 ? (target - t) : (t - target); - if(d > 500000) d = 1000000-d; - if(d <= tol && d >= -tol) { - return(1); - } else { - return(0); - } -} -#endif /* HIRESTIME */