| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /*- |
| 2 | * Copyright (c) 1992, 1993, 1995 Eugene W. Stark | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * 3. All advertising materials mentioning features or use of this software | |
| 14 | * must display the following acknowledgement: | |
| 15 | * This product includes software developed by Eugene W. Stark. | |
| 16 | * 4. The name of the author may not be used to endorse or promote products | |
| 17 | * derived from this software without specific prior written permission. | |
| 18 | * | |
| 19 | * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND | |
| 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
| 23 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 29 | * SUCH DAMAGE. | |
| 30 | * | |
| 31 | * $FreeBSD: src/sys/i386/isa/tw.c,v 1.38 2000/01/29 16:00:32 peter Exp $ | |
| 21ce0dfa | 32 | * $DragonFly: src/sys/dev/misc/tw/tw.c,v 1.19 2008/08/02 01:14:42 dillon Exp $ |
| 984263bc MD |
33 | * |
| 34 | */ | |
| 35 | ||
| 1f2de5d4 | 36 | #include "use_tw.h" |
| 984263bc MD |
37 | |
| 38 | /* | |
| 39 | * Driver configuration parameters | |
| 40 | */ | |
| 41 | ||
| 42 | /* | |
| 43 | * Time for 1/2 of a power line cycle, in microseconds. | |
| 44 | * Change this to 10000 for 50Hz power. Phil Sampson | |
| 45 | * (vk2jnt@gw.vk2jnt.ampr.org OR sampson@gidday.enet.dec.com) | |
| 46 | * reports that this works (at least in Australia) using a | |
| 47 | * TW7223 module (a local version of the TW523). | |
| 48 | */ | |
| 49 | #define HALFCYCLE 8333 /* 1/2 cycle = 8333us at 60Hz */ | |
| 50 | ||
| 51 | /* | |
| 52 | * Undefine the following if you don't have the high-resolution "microtime" | |
| 53 | * routines (leave defined for FreeBSD, which has them). | |
| 54 | */ | |
| 55 | #define HIRESTIME | |
| 56 | ||
| 57 | /* | |
| 58 | * End of driver configuration parameters | |
| 59 | */ | |
| 60 | ||
| 61 | /* | |
| 62 | * FreeBSD Device Driver for X-10 POWERHOUSE (tm) | |
| 63 | * Two-Way Power Line Interface, Model #TW523 | |
| 64 | * | |
| 65 | * written by Eugene W. Stark (stark@cs.sunysb.edu) | |
| 66 | * December 2, 1992 | |
| 67 | * | |
| 68 | * NOTES: | |
| 69 | * | |
| 70 | * The TW523 is a carrier-current modem for home control/automation purposes. | |
| 71 | * It is made by: | |
| 72 | * | |
| 73 | * X-10 Inc. | |
| 74 | * 185A LeGrand Ave. | |
| 75 | * Northvale, NJ 07647 | |
| 76 | * USA | |
| 77 | * (201) 784-9700 or 1-800-526-0027 | |
| 78 | * | |
| 79 | * X-10 Home Controls Inc. | |
| 80 | * 1200 Aerowood Drive, Unit 20 | |
| 81 | * Mississauga, Ontario | |
| 82 | * (416) 624-4446 or 1-800-387-3346 | |
| 83 | * | |
| 84 | * The TW523 is designed for communications using the X-10 protocol, | |
| 85 | * which is compatible with a number of home control systems, including | |
| 86 | * Radio Shack "Plug 'n Power(tm)" and Stanley "Lightmaker(tm)." | |
| 87 | * I bought my TW523 from: | |
| 88 | * | |
| 89 | * Home Control Concepts | |
| 90 | * 9353-C Activity Road | |
| 91 | * San Diego, CA 92126 | |
| 92 | * (619) 693-8887 | |
| 93 | * | |
| 94 | * They supplied me with the TW523 (which has an RJ-11 four-wire modular | |
| 95 | * telephone connector), a modular cable, an RJ-11 to DB-25 connector with | |
| 96 | * internal wiring, documentation from X-10 on the TW523 (very good), | |
| 97 | * an instruction manual by Home Control Concepts (not very informative), | |
| 98 | * and a floppy disk containing binary object code of some demonstration/test | |
| 99 | * programs and of a C function library suitable for controlling the TW523 | |
| 100 | * by an IBM PC under MS-DOS (not useful to me other than to verify that | |
| 101 | * the unit worked). I suggest saving money and buying the bare TW523 | |
| 102 | * rather than the TW523 development kit (what I bought), because if you | |
| 103 | * are running FreeBSD you don't really care about the DOS binaries. | |
| 104 | * | |
| 105 | * The interface to the TW-523 consists of four wires on the RJ-11 connector, | |
| 106 | * which are jumpered to somewhat more wires on the DB-25 connector, which | |
| 107 | * in turn is intended to plug into the PC parallel printer port. I dismantled | |
| 108 | * the DB-25 connector to find out what they had done: | |
| 109 | * | |
| 110 | * Signal RJ-11 pin DB-25 pin(s) Parallel Port | |
| 111 | * Transmit TX 4 (Y) 2, 4, 6, 8 Data out | |
| 112 | * Receive RX 3 (G) 10, 14 -ACK, -AutoFeed | |
| 113 | * Common 2 (R) 25 Common | |
| 114 | * Zero crossing 1 (B) 17 or 12 -Select or +PaperEnd | |
| 115 | * | |
| 116 | * NOTE: In the original cable I have (which I am still using, May, 1997) | |
| 117 | * the Zero crossing signal goes to pin 17 (-Select) on the parallel port. | |
| 118 | * In retrospect, this doesn't make a whole lot of sense, given that the | |
| 119 | * -Select signal propagates the other direction. Indeed, some people have | |
| 120 | * reported problems with this, and have had success using pin 12 (+PaperEnd) | |
| 121 | * instead. This driver searches for the zero crossing signal on either | |
| 122 | * pin 17 or pin 12, so it should work with either cable configuration. | |
| 123 | * My suggestion would be to start by making the cable so that the zero | |
| 124 | * crossing signal goes to pin 12 on the parallel port. | |
| 125 | * | |
| 126 | * The zero crossing signal is used to synchronize transmission to the | |
| 127 | * zero crossings of the AC line, as detailed in the X-10 documentation. | |
| 128 | * It would be nice if one could generate interrupts with this signal, | |
| 129 | * however one needs interrupts on both the rising and falling edges, | |
| 130 | * and the -ACK signal to the parallel port interrupts only on the falling | |
| 131 | * edge, so it can't be done without additional hardware. | |
| 132 | * | |
| 133 | * In this driver, the transmit function is performed in a non-interrupt-driven | |
| 134 | * fashion, by polling the zero crossing signal to determine when a transition | |
| 135 | * has occurred. This wastes CPU time during transmission, but it seems like | |
| 136 | * the best that can be done without additional hardware. One problem with | |
| 137 | * the scheme is that preemption of the CPU during transmission can cause loss | |
| 138 | * of sync. The driver tries to catch this, by noticing that a long delay | |
| 139 | * loop has somehow become foreshortened, and the transmission is aborted with | |
| 140 | * an error return. It is up to the user level software to handle this | |
| 141 | * situation (most likely by retrying the transmission). | |
| 142 | */ | |
| 143 | ||
| 144 | #include <sys/param.h> | |
| 145 | #include <sys/systm.h> | |
| 146 | #include <sys/conf.h> | |
| fef8985e | 147 | #include <sys/device.h> |
| 984263bc MD |
148 | #include <sys/kernel.h> |
| 149 | #include <sys/uio.h> | |
| 150 | #include <sys/syslog.h> | |
| 9183fa5b | 151 | #include <sys/selinfo.h> |
| 984263bc | 152 | #include <sys/poll.h> |
| 8ba87358 | 153 | #include <sys/thread2.h> |
| 984263bc MD |
154 | |
| 155 | #ifdef HIRESTIME | |
| 156 | #include <sys/time.h> | |
| 157 | #endif /* HIRESTIME */ | |
| 158 | ||
| 21ce0dfa | 159 | #include <bus/isa/isa_device.h> |
| 984263bc MD |
160 | |
| 161 | /* | |
| 162 | * Transmission is done by calling write() to send three byte packets of data. | |
| 163 | * The first byte contains a four bit house code (0=A to 15=P). | |
| 164 | * The second byte contains five bit unit/key code (0=unit 1 to 15=unit 16, | |
| 165 | * 16=All Units Off to 31 = Status Request). The third byte specifies | |
| 166 | * the number of times the packet is to be transmitted without any | |
| 167 | * gaps between successive transmissions. Normally this is 2, as per | |
| 168 | * the X-10 documentation, but sometimes (e.g. for bright and dim codes) | |
| 169 | * it can be another value. Each call to write can specify an arbitrary | |
| 170 | * number of data bytes. An incomplete packet is buffered until a subsequent | |
| 171 | * call to write() provides data to complete it. At most one packet will | |
| 172 | * actually be processed in any call to write(). Successive calls to write() | |
| 173 | * leave a three-cycle gap between transmissions, per the X-10 documentation. | |
| 174 | * | |
| 175 | * Reception is done using read(). | |
| 176 | * The driver produces a series of three-character packets. | |
| 177 | * In each packet, the first character consists of flags, | |
| 178 | * the second character is a four bit house code (0-15), | |
| 179 | * and the third character is a five bit key/function code (0-31). | |
| 180 | * The flags are the following: | |
| 181 | */ | |
| 182 | ||
| 183 | #define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */ | |
| 184 | #define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */ | |
| 185 | ||
| 186 | /* | |
| 187 | * IBM PC parallel port definitions relevant to TW523 | |
| 188 | */ | |
| 189 | ||
| 190 | #define tw_data 0 /* Data to tw523 (R/W) */ | |
| 191 | ||
| 192 | #define tw_status 1 /* Status of tw523 (R) */ | |
| 193 | #define TWS_RDATA 0x40 /* tw523 receive data */ | |
| 194 | #define TWS_OUT 0x20 /* pin 12, out of paper */ | |
| 195 | ||
| 196 | #define tw_control 2 /* Control tw523 (R/W) */ | |
| 197 | #define TWC_SYNC 0x08 /* tw523 sync (pin 17) */ | |
| 198 | #define TWC_ENA 0x10 /* tw523 interrupt enable */ | |
| 199 | ||
| 200 | /* | |
| 201 | * Miscellaneous defines | |
| 202 | */ | |
| 203 | ||
| 204 | #define TWUNIT(dev) (minor(dev)) /* Extract unit number from device */ | |
| 984263bc MD |
205 | |
| 206 | static int twprobe(struct isa_device *idp); | |
| 207 | static int twattach(struct isa_device *idp); | |
| 208 | ||
| 209 | struct isa_driver twdriver = { | |
| 210 | twprobe, twattach, "tw" | |
| 211 | }; | |
| 212 | ||
| 213 | static d_open_t twopen; | |
| 214 | static d_close_t twclose; | |
| 215 | static d_read_t twread; | |
| 216 | static d_write_t twwrite; | |
| 217 | static d_poll_t twpoll; | |
| 218 | ||
| 219 | #define CDEV_MAJOR 19 | |
| fef8985e MD |
220 | static struct dev_ops tw_ops = { |
| 221 | { "tw", CDEV_MAJOR, 0 }, | |
| 222 | .d_open = twopen, | |
| 223 | .d_close = twclose, | |
| 224 | .d_read = twread, | |
| 225 | .d_write = twwrite, | |
| 226 | .d_poll = twpoll, | |
| 984263bc MD |
227 | }; |
| 228 | ||
| 229 | /* | |
| 230 | * Software control structure for TW523 | |
| 231 | */ | |
| 232 | ||
| 233 | #define TWS_XMITTING 1 /* Transmission in progress */ | |
| 234 | #define TWS_RCVING 2 /* Reception in progress */ | |
| 235 | #define TWS_WANT 4 /* A process wants received data */ | |
| 236 | #define TWS_OPEN 8 /* Is it currently open? */ | |
| 237 | ||
| 238 | #define TW_SIZE 3*60 /* Enough for about 10 sec. of input */ | |
| 239 | #define TW_MIN_DELAY 1500 /* Ignore interrupts of lesser latency */ | |
| 240 | ||
| 241 | static struct tw_sc { | |
| 242 | u_int sc_port; /* I/O Port */ | |
| 243 | u_int sc_state; /* Current software control state */ | |
| 244 | struct selinfo sc_selp; /* Information for select() */ | |
| 245 | u_char sc_xphase; /* Current state of sync (for transmitter) */ | |
| 246 | u_char sc_rphase; /* Current state of sync (for receiver) */ | |
| 247 | u_char sc_flags; /* Flags for current reception */ | |
| 248 | short sc_rcount; /* Number of bits received so far */ | |
| 249 | int sc_bits; /* Bits received so far */ | |
| 250 | u_char sc_pkt[3]; /* Packet not yet transmitted */ | |
| 251 | short sc_pktsize; /* How many bytes in the packet? */ | |
| 252 | u_char sc_buf[TW_SIZE]; /* We buffer our own input */ | |
| 253 | int sc_nextin; /* Next free slot in circular buffer */ | |
| 254 | int sc_nextout; /* First used slot in circular buffer */ | |
| 255 | /* Callout for canceling our abortrcv timeout */ | |
| 726b8254 | 256 | struct callout abortrcv_ch; |
| 984263bc MD |
257 | #ifdef HIRESTIME |
| 258 | int sc_xtimes[22]; /* Times for bits in current xmit packet */ | |
| 259 | int sc_rtimes[22]; /* Times for bits in current rcv packet */ | |
| 260 | int sc_no_rcv; /* number of interrupts received */ | |
| 261 | #define SC_RCV_TIME_LEN 128 | |
| 262 | int sc_rcv_time[SC_RCV_TIME_LEN]; /* usec time stamp on interrupt */ | |
| 263 | #endif /* HIRESTIME */ | |
| 264 | } tw_sc[NTW]; | |
| 265 | ||
| 266 | static int tw_zcport; /* offset of port for zero crossing signal */ | |
| 267 | static int tw_zcmask; /* mask for the zero crossing signal */ | |
| 268 | ||
| 269 | static void twdelay25(void); | |
| 270 | static void twdelayn(int n); | |
| 271 | static void twsetuptimes(int *a); | |
| 272 | static int wait_for_zero(struct tw_sc *sc); | |
| 273 | static int twputpkt(struct tw_sc *sc, u_char *p); | |
| 1b51b0fa | 274 | static void twintr(void *); |
| 984263bc MD |
275 | static int twgetbytes(struct tw_sc *sc, u_char *p, int cnt); |
| 276 | static timeout_t twabortrcv; | |
| 277 | static int twsend(struct tw_sc *sc, int h, int k, int cnt); | |
| 278 | static int next_zero(struct tw_sc *sc); | |
| 279 | static int twchecktime(int target, int tol); | |
| 280 | static void twdebugtimes(struct tw_sc *sc); | |
| 281 | ||
| 282 | /* | |
| 283 | * Counter value for delay loop. | |
| 284 | * It is adjusted by twprobe so that the delay loop takes about 25us. | |
| 285 | */ | |
| 286 | ||
| 287 | #define TWDELAYCOUNT 161 /* Works on my 486DX/33 */ | |
| 288 | static int twdelaycount; | |
| 289 | ||
| 290 | /* | |
| 291 | * Twdelay25 is used for very short delays of about 25us. | |
| 292 | * It is implemented with a calibrated delay loop, and should be | |
| 293 | * fairly accurate ... unless we are preempted by an interrupt. | |
| 294 | * | |
| 295 | * We use this to wait for zero crossings because the X-10 specs say we | |
| 296 | * are supposed to assert carrier within 25us when one happens. | |
| 297 | * I don't really believe we can do this, but the X-10 devices seem to be | |
| 298 | * fairly forgiving. | |
| 299 | */ | |
| 300 | ||
| c436375a SW |
301 | static void |
| 302 | twdelay25(void) | |
| 984263bc MD |
303 | { |
| 304 | int cnt; | |
| 305 | for(cnt = twdelaycount; cnt; cnt--); /* Should take about 25us */ | |
| 306 | } | |
| 307 | ||
| 308 | /* | |
| 309 | * Twdelayn is used to time the length of the 1ms carrier pulse. | |
| 310 | * This is not very critical, but if we have high-resolution time-of-day | |
| 311 | * we check it every apparent 200us to make sure we don't get too far off | |
| 312 | * if we happen to be interrupted during the delay. | |
| 313 | */ | |
| 314 | ||
| c436375a SW |
315 | static void |
| 316 | twdelayn(int n) | |
| 984263bc MD |
317 | { |
| 318 | #ifdef HIRESTIME | |
| 319 | int t, d; | |
| 320 | struct timeval tv; | |
| 321 | microtime(&tv); | |
| 322 | t = tv.tv_usec; | |
| 323 | t += n; | |
| 324 | #endif /* HIRESTIME */ | |
| 325 | while(n > 0) { | |
| 326 | twdelay25(); | |
| 327 | n -= 25; | |
| 328 | #ifdef HIRESTIME | |
| 329 | if((n & 0x7) == 0) { | |
| 330 | microtime(&tv); | |
| 331 | d = tv.tv_usec - t; | |
| 332 | if(d >= 0 && d < 1000000) return; | |
| 333 | } | |
| 334 | #endif /* HIRESTIME */ | |
| 335 | } | |
| 336 | } | |
| 337 | ||
| c436375a SW |
338 | static int |
| 339 | twprobe(struct isa_device *idp) | |
| 984263bc MD |
340 | { |
| 341 | struct tw_sc sc; | |
| 342 | int d; | |
| 343 | int tries; | |
| 984263bc | 344 | |
| 984263bc MD |
345 | sc.sc_port = idp->id_iobase; |
| 346 | /* Search for the zero crossing signal at ports, bit combinations. */ | |
| 347 | tw_zcport = tw_control; | |
| 348 | tw_zcmask = TWC_SYNC; | |
| 349 | sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask; | |
| 350 | if(wait_for_zero(&sc) < 0) { | |
| 351 | tw_zcport = tw_status; | |
| 352 | tw_zcmask = TWS_OUT; | |
| 353 | sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask; | |
| 354 | } | |
| 355 | if(wait_for_zero(&sc) < 0) | |
| 356 | return(0); | |
| 357 | /* | |
| 358 | * Iteratively check the timing of a few sync transitions, and adjust | |
| 359 | * the loop delay counter, if necessary, to bring the timing reported | |
| 360 | * by wait_for_zero() close to HALFCYCLE. Give up if anything | |
| 361 | * ridiculous happens. | |
| 362 | */ | |
| 363 | if(twdelaycount == 0) { /* Only adjust timing for first unit */ | |
| 364 | twdelaycount = TWDELAYCOUNT; | |
| 365 | for(tries = 0; tries < 10; tries++) { | |
| 366 | sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask; | |
| 367 | if(wait_for_zero(&sc) >= 0) { | |
| 368 | d = wait_for_zero(&sc); | |
| 369 | if(d <= HALFCYCLE/100 || d >= HALFCYCLE*100) { | |
| 370 | twdelaycount = 0; | |
| 371 | return(0); | |
| 372 | } | |
| 373 | twdelaycount = (twdelaycount * d)/HALFCYCLE; | |
| 374 | } | |
| 375 | } | |
| 376 | } | |
| 377 | /* | |
| 378 | * Now do a final check, just to make sure | |
| 379 | */ | |
| 380 | sc.sc_xphase = inb(idp->id_iobase + tw_zcport) & tw_zcmask; | |
| 381 | if(wait_for_zero(&sc) >= 0) { | |
| 382 | d = wait_for_zero(&sc); | |
| 383 | if(d <= (HALFCYCLE * 110)/100 && d >= (HALFCYCLE * 90)/100) return(8); | |
| 384 | } | |
| 385 | return(0); | |
| 386 | } | |
| 387 | ||
| c436375a SW |
388 | static int |
| 389 | twattach(struct isa_device *idp) | |
| 984263bc MD |
390 | { |
| 391 | struct tw_sc *sc; | |
| 392 | int unit; | |
| 393 | ||
| 1b51b0fa | 394 | idp->id_intr = (inthand2_t *)twintr; |
| 984263bc MD |
395 | sc = &tw_sc[unit = idp->id_unit]; |
| 396 | sc->sc_port = idp->id_iobase; | |
| 397 | sc->sc_state = 0; | |
| 398 | sc->sc_rcount = 0; | |
| 726b8254 | 399 | callout_init(&sc->abortrcv_ch); |
| fef8985e | 400 | make_dev(&tw_ops, unit, 0, 0, 0600, "tw%d", unit); |
| 984263bc MD |
401 | return (1); |
| 402 | } | |
| 403 | ||
| c436375a | 404 | int |
| fef8985e | 405 | twopen(struct dev_open_args *ap) |
| 984263bc | 406 | { |
| b13267a5 | 407 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 408 | struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; |
| 984263bc | 409 | |
| 8ba87358 | 410 | crit_enter(); |
| 984263bc MD |
411 | if(sc->sc_state == 0) { |
| 412 | sc->sc_state = TWS_OPEN; | |
| 413 | sc->sc_nextin = sc->sc_nextout = 0; | |
| 414 | sc->sc_pktsize = 0; | |
| 415 | outb(sc->sc_port+tw_control, TWC_ENA); | |
| 416 | } | |
| 8ba87358 | 417 | crit_exit(); |
| 984263bc MD |
418 | return(0); |
| 419 | } | |
| 420 | ||
| c436375a | 421 | int |
| fef8985e | 422 | twclose(struct dev_close_args *ap) |
| 984263bc | 423 | { |
| b13267a5 | 424 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 425 | struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; |
| 984263bc | 426 | |
| 8ba87358 | 427 | crit_enter(); |
| 984263bc MD |
428 | sc->sc_state = 0; |
| 429 | outb(sc->sc_port+tw_control, 0); | |
| 8ba87358 | 430 | crit_exit(); |
| 984263bc MD |
431 | return(0); |
| 432 | } | |
| 433 | ||
| c436375a | 434 | int |
| fef8985e | 435 | twread(struct dev_read_args *ap) |
| 984263bc | 436 | { |
| b13267a5 | 437 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 438 | struct uio *uio = ap->a_uio; |
| 984263bc MD |
439 | u_char buf[3]; |
| 440 | struct tw_sc *sc = &tw_sc[TWUNIT(dev)]; | |
| 8ba87358 | 441 | int error, cnt; |
| 984263bc | 442 | |
| 8ba87358 | 443 | crit_enter(); |
| 984263bc MD |
444 | cnt = MIN(uio->uio_resid, 3); |
| 445 | if((error = twgetbytes(sc, buf, cnt)) == 0) { | |
| 446 | error = uiomove(buf, cnt, uio); | |
| 447 | } | |
| 8ba87358 | 448 | crit_exit(); |
| 984263bc MD |
449 | return(error); |
| 450 | } | |
| 451 | ||
| c436375a | 452 | int |
| fef8985e | 453 | twwrite(struct dev_write_args *ap) |
| 984263bc | 454 | { |
| b13267a5 | 455 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 456 | struct uio *uio = ap->a_uio; |
| 984263bc MD |
457 | struct tw_sc *sc; |
| 458 | int house, key, reps; | |
| 8ba87358 | 459 | int error; |
| 984263bc MD |
460 | int cnt; |
| 461 | ||
| 462 | sc = &tw_sc[TWUNIT(dev)]; | |
| 463 | /* | |
| 464 | * Note: Although I had intended to allow concurrent transmitters, | |
| 465 | * there is a potential problem here if two processes both write | |
| 466 | * into the sc_pkt buffer at the same time. The following code | |
| 467 | * is an additional critical section that needs to be synchronized. | |
| 468 | */ | |
| 8ba87358 | 469 | crit_enter(); |
| 984263bc MD |
470 | cnt = MIN(3 - sc->sc_pktsize, uio->uio_resid); |
| 471 | error = uiomove(&(sc->sc_pkt[sc->sc_pktsize]), cnt, uio); | |
| 472 | if(error) { | |
| 8ba87358 | 473 | crit_exit(); |
| 984263bc MD |
474 | return(error); |
| 475 | } | |
| 476 | sc->sc_pktsize += cnt; | |
| 477 | if(sc->sc_pktsize < 3) { /* Only transmit 3-byte packets */ | |
| 8ba87358 | 478 | crit_exit(); |
| 984263bc MD |
479 | return(0); |
| 480 | } | |
| 481 | sc->sc_pktsize = 0; | |
| 482 | /* | |
| 483 | * Collect house code, key code, and rep count, and check for sanity. | |
| 484 | */ | |
| 485 | house = sc->sc_pkt[0]; | |
| 486 | key = sc->sc_pkt[1]; | |
| 487 | reps = sc->sc_pkt[2]; | |
| 488 | if(house >= 16 || key >= 32) { | |
| 8ba87358 | 489 | crit_exit(); |
| 984263bc MD |
490 | return(ENODEV); |
| 491 | } | |
| 492 | /* | |
| 493 | * Synchronize with the receiver operating in the bottom half, and | |
| 494 | * also with concurrent transmitters. | |
| 495 | * We don't want to interfere with a packet currently being received, | |
| 496 | * and we would like the receiver to recognize when a packet has | |
| 497 | * originated locally. | |
| 498 | */ | |
| 499 | while(sc->sc_state & (TWS_RCVING | TWS_XMITTING)) { | |
| 377d4740 | 500 | error = tsleep((caddr_t)sc, PCATCH, "twwrite", 0); |
| 984263bc | 501 | if(error) { |
| 8ba87358 | 502 | crit_exit(); |
| 984263bc MD |
503 | return(error); |
| 504 | } | |
| 505 | } | |
| 506 | sc->sc_state |= TWS_XMITTING; | |
| 507 | /* | |
| 508 | * Everything looks OK, let's do the transmission. | |
| 509 | */ | |
| 8ba87358 | 510 | crit_exit(); /* Enable interrupts because this takes a LONG time */ |
| 984263bc | 511 | error = twsend(sc, house, key, reps); |
| 8ba87358 | 512 | crit_enter(); |
| 984263bc MD |
513 | sc->sc_state &= ~TWS_XMITTING; |
| 514 | wakeup((caddr_t)sc); | |
| 8ba87358 | 515 | crit_exit(); |
| 984263bc MD |
516 | if(error) return(EIO); |
| 517 | else return(0); | |
| 518 | } | |
| 519 | ||
| 520 | /* | |
| 521 | * Determine if there is data available for reading | |
| 522 | */ | |
| 523 | ||
| c436375a | 524 | int |
| fef8985e | 525 | twpoll(struct dev_poll_args *ap) |
| 984263bc | 526 | { |
| b13267a5 | 527 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 528 | struct tw_sc *sc; |
| 984263bc MD |
529 | int revents = 0; |
| 530 | ||
| 531 | sc = &tw_sc[TWUNIT(dev)]; | |
| 8ba87358 | 532 | crit_enter(); |
| 984263bc | 533 | /* XXX is this correct? the original code didn't test select rw mode!! */ |
| fef8985e | 534 | if (ap->a_events & (POLLIN | POLLRDNORM)) { |
| 984263bc | 535 | if(sc->sc_nextin != sc->sc_nextout) |
| fef8985e | 536 | revents |= ap->a_events & (POLLIN | POLLRDNORM); |
| 984263bc | 537 | else |
| fef8985e | 538 | selrecord(curthread, &sc->sc_selp); |
| 984263bc | 539 | } |
| 8ba87358 | 540 | crit_exit(); |
| fef8985e MD |
541 | ap->a_events = revents; |
| 542 | return(0); | |
| 984263bc MD |
543 | } |
| 544 | ||
| 545 | /* | |
| 546 | * X-10 Protocol | |
| 547 | */ | |
| 548 | ||
| 549 | #define X10_START_LENGTH 4 | |
| 550 | static char X10_START[] = { 1, 1, 1, 0 }; | |
| 551 | ||
| 552 | /* | |
| 553 | * Each bit of the 4-bit house code and 5-bit key code | |
| 554 | * is transmitted twice, once in true form, and then in | |
| 555 | * complemented form. This is already taken into account | |
| 556 | * in the following tables. | |
| 557 | */ | |
| 558 | ||
| 559 | #define X10_HOUSE_LENGTH 8 | |
| 560 | static char X10_HOUSE[16][8] = { | |
| f15db79e MD |
561 | { 0, 1, 1, 0, 1, 0, 0, 1 }, /* A = 0110 */ |
| 562 | { 1, 0, 1, 0, 1, 0, 0, 1 }, /* B = 1110 */ | |
| 563 | { 0, 1, 0, 1, 1, 0, 0, 1 }, /* C = 0010 */ | |
| 564 | { 1, 0, 0, 1, 1, 0, 0, 1 }, /* D = 1010 */ | |
| 565 | { 0, 1, 0, 1, 0, 1, 1, 0 }, /* E = 0001 */ | |
| 566 | { 1, 0, 0, 1, 0, 1, 1, 0 }, /* F = 1001 */ | |
| 567 | { 0, 1, 1, 0, 0, 1, 1, 0 }, /* G = 0101 */ | |
| 568 | { 1, 0, 1, 0, 0, 1, 1, 0 }, /* H = 1101 */ | |
| 569 | { 0, 1, 1, 0, 1, 0, 1, 0 }, /* I = 0111 */ | |
| 570 | { 1, 0, 1, 0, 1, 0, 1, 0 }, /* J = 1111 */ | |
| 571 | { 0, 1, 0, 1, 1, 0, 1, 0 }, /* K = 0011 */ | |
| 572 | { 1, 0, 0, 1, 1, 0, 1, 0 }, /* L = 1011 */ | |
| 573 | { 0, 1, 0, 1, 0, 1, 0, 1 }, /* M = 0000 */ | |
| 574 | { 1, 0, 0, 1, 0, 1, 0, 1 }, /* N = 1000 */ | |
| 575 | { 0, 1, 1, 0, 0, 1, 0, 1 }, /* O = 0100 */ | |
| 576 | { 1, 0, 1, 0, 0, 1, 0, 1 } /* P = 1100 */ | |
| 984263bc MD |
577 | }; |
| 578 | ||
| 579 | #define X10_KEY_LENGTH 10 | |
| 580 | static char X10_KEY[32][10] = { | |
| f15db79e MD |
581 | { 0, 1, 1, 0, 1, 0, 0, 1, 0, 1 }, /* 01100 => 1 */ |
| 582 | { 1, 0, 1, 0, 1, 0, 0, 1, 0, 1 }, /* 11100 => 2 */ | |
| 583 | { 0, 1, 0, 1, 1, 0, 0, 1, 0, 1 }, /* 00100 => 3 */ | |
| 584 | { 1, 0, 0, 1, 1, 0, 0, 1, 0, 1 }, /* 10100 => 4 */ | |
| 585 | { 0, 1, 0, 1, 0, 1, 1, 0, 0, 1 }, /* 00010 => 5 */ | |
| 586 | { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1 }, /* 10010 => 6 */ | |
| 587 | { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1 }, /* 01010 => 7 */ | |
| 588 | { 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 }, /* 11010 => 8 */ | |
| 589 | { 0, 1, 1, 0, 1, 0, 1, 0, 0, 1 }, /* 01110 => 9 */ | |
| 590 | { 1, 0, 1, 0, 1, 0, 1, 0, 0, 1 }, /* 11110 => 10 */ | |
| 591 | { 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 }, /* 00110 => 11 */ | |
| 592 | { 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 }, /* 10110 => 12 */ | |
| 593 | { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, /* 00000 => 13 */ | |
| 594 | { 1, 0, 0, 1, 0, 1, 0, 1, 0, 1 }, /* 10000 => 14 */ | |
| 595 | { 0, 1, 1, 0, 0, 1, 0, 1, 0, 1 }, /* 01000 => 15 */ | |
| 596 | { 1, 0, 1, 0, 0, 1, 0, 1, 0, 1 }, /* 11000 => 16 */ | |
| 597 | { 0, 1, 0, 1, 0, 1, 0, 1, 1, 0 }, /* 00001 => All Units Off */ | |
| 598 | { 0, 1, 0, 1, 0, 1, 1, 0, 1, 0 }, /* 00011 => All Units On */ | |
| 599 | { 0, 1, 0, 1, 1, 0, 0, 1, 1, 0 }, /* 00101 => On */ | |
| 600 | { 0, 1, 0, 1, 1, 0, 1, 0, 1, 0 }, /* 00111 => Off */ | |
| 601 | { 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }, /* 01001 => Dim */ | |
| 602 | { 0, 1, 1, 0, 0, 1, 1, 0, 1, 0 }, /* 01011 => Bright */ | |
| 603 | { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0 }, /* 01101 => All LIGHTS Off */ | |
| 604 | { 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 }, /* 01111 => Extended Code */ | |
| 605 | { 1, 0, 0, 1, 0, 1, 0, 1, 1, 0 }, /* 10001 => Hail Request */ | |
| 606 | { 1, 0, 0, 1, 0, 1, 1, 0, 1, 0 }, /* 10011 => Hail Acknowledge */ | |
| 607 | { 1, 0, 0, 1, 1, 0, 0, 1, 1, 0 }, /* 10101 => Preset Dim 0 */ | |
| 608 | { 1, 0, 0, 1, 1, 0, 1, 0, 1, 0 }, /* 10111 => Preset Dim 1 */ | |
| 609 | { 1, 0, 1, 0, 0, 1, 0, 1, 0, 1 }, /* 11000 => Extended Data (analog) */ | |
| 610 | { 1, 0, 1, 0, 0, 1, 1, 0, 1, 0 }, /* 11011 => Status = on */ | |
| 611 | { 1, 0, 1, 0, 1, 0, 0, 1, 1, 0 }, /* 11101 => Status = off */ | |
| 612 | { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 } /* 11111 => Status request */ | |
| 984263bc MD |
613 | }; |
| 614 | ||
| 615 | /* | |
| 616 | * Tables for mapping received X-10 code back to house/key number. | |
| 617 | */ | |
| 618 | ||
| 619 | static short X10_HOUSE_INV[16] = { | |
| 620 | 12, 4, 2, 10, 14, 6, 0, 8, | |
| 621 | 13, 5, 3, 11, 15, 7, 1, 9 | |
| 622 | }; | |
| 623 | ||
| 624 | static short X10_KEY_INV[32] = { | |
| 625 | 12, 16, 4, 17, 2, 18, 10, 19, | |
| 626 | 14, 20, 6, 21, 0, 22, 8, 23, | |
| 627 | 13, 24, 5, 25, 3, 26, 11, 27, | |
| 628 | 15, 28, 7, 29, 1, 30, 9, 31 | |
| 629 | }; | |
| 630 | ||
| 631 | static char *X10_KEY_LABEL[32] = { | |
| 632 | "1", | |
| 633 | "2", | |
| 634 | "3", | |
| 635 | "4", | |
| 636 | "5", | |
| 637 | "6", | |
| 638 | "7", | |
| 639 | "8", | |
| 640 | "9", | |
| 641 | "10", | |
| 642 | "11", | |
| 643 | "12", | |
| 644 | "13", | |
| 645 | "14", | |
| 646 | "15", | |
| 647 | "16", | |
| 648 | "All Units Off", | |
| 649 | "All Units On", | |
| 650 | "On", | |
| 651 | "Off", | |
| 652 | "Dim", | |
| 653 | "Bright", | |
| 654 | "All LIGHTS Off", | |
| 655 | "Extended Code", | |
| 656 | "Hail Request", | |
| 657 | "Hail Acknowledge", | |
| 658 | "Preset Dim 0", | |
| 659 | "Preset Dim 1", | |
| 660 | "Extended Data (analog)", | |
| 661 | "Status = on", | |
| 662 | "Status = off", | |
| 663 | "Status request" | |
| 664 | }; | |
| 665 | /* | |
| 666 | * Transmit a packet containing house code h and key code k | |
| 667 | */ | |
| 668 | ||
| 669 | #define TWRETRY 10 /* Try 10 times to sync with AC line */ | |
| 670 | ||
| c436375a SW |
671 | static int |
| 672 | twsend(struct tw_sc *sc, int h, int k, int cnt) | |
| 984263bc MD |
673 | { |
| 674 | int i; | |
| 675 | int port = sc->sc_port; | |
| 676 | ||
| 677 | /* | |
| 678 | * Make sure we get a reliable sync with a power line zero crossing | |
| 679 | */ | |
| 680 | for(i = 0; i < TWRETRY; i++) { | |
| 681 | if(wait_for_zero(sc) > 100) goto insync; | |
| 682 | } | |
| 683 | log(LOG_ERR, "TWXMIT: failed to sync.\n"); | |
| 684 | return(-1); | |
| 685 | ||
| 686 | insync: | |
| 687 | /* | |
| 688 | * Be sure to leave 3 cycles space between transmissions | |
| 689 | */ | |
| 690 | for(i = 6; i > 0; i--) | |
| 691 | if(next_zero(sc) < 0) return(-1); | |
| 692 | /* | |
| 693 | * The packet is transmitted cnt times, with no gaps. | |
| 694 | */ | |
| 695 | while(cnt--) { | |
| 696 | /* | |
| 697 | * Transmit the start code | |
| 698 | */ | |
| 699 | for(i = 0; i < X10_START_LENGTH; i++) { | |
| 700 | outb(port+tw_data, X10_START[i] ? 0xff : 0x00); /* Waste no time! */ | |
| 701 | #ifdef HIRESTIME | |
| 702 | if(i == 0) twsetuptimes(sc->sc_xtimes); | |
| 703 | if(twchecktime(sc->sc_xtimes[i], HALFCYCLE/20) == 0) { | |
| 704 | outb(port+tw_data, 0); | |
| 705 | return(-1); | |
| 706 | } | |
| 707 | #endif /* HIRESTIME */ | |
| 708 | twdelayn(1000); /* 1ms pulse width */ | |
| 709 | outb(port+tw_data, 0); | |
| 710 | if(next_zero(sc) < 0) return(-1); | |
| 711 | } | |
| 712 | /* | |
| 713 | * Transmit the house code | |
| 714 | */ | |
| 715 | for(i = 0; i < X10_HOUSE_LENGTH; i++) { | |
| 716 | outb(port+tw_data, X10_HOUSE[h][i] ? 0xff : 0x00); /* Waste no time! */ | |
| 717 | #ifdef HIRESTIME | |
| 718 | if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH], HALFCYCLE/20) == 0) { | |
| 719 | outb(port+tw_data, 0); | |
| 720 | return(-1); | |
| 721 | } | |
| 722 | #endif /* HIRESTIME */ | |
| 723 | twdelayn(1000); /* 1ms pulse width */ | |
| 724 | outb(port+tw_data, 0); | |
| 725 | if(next_zero(sc) < 0) return(-1); | |
| 726 | } | |
| 727 | /* | |
| 728 | * Transmit the unit/key code | |
| 729 | */ | |
| 730 | for(i = 0; i < X10_KEY_LENGTH; i++) { | |
| 731 | outb(port+tw_data, X10_KEY[k][i] ? 0xff : 0x00); | |
| 732 | #ifdef HIRESTIME | |
| 733 | if(twchecktime(sc->sc_xtimes[i+X10_START_LENGTH+X10_HOUSE_LENGTH], | |
| 734 | HALFCYCLE/20) == 0) { | |
| 735 | outb(port+tw_data, 0); | |
| 736 | return(-1); | |
| 737 | } | |
| 738 | #endif /* HIRESTIME */ | |
| 739 | twdelayn(1000); /* 1ms pulse width */ | |
| 740 | outb(port+tw_data, 0); | |
| 741 | if(next_zero(sc) < 0) return(-1); | |
| 742 | } | |
| 743 | } | |
| 744 | return(0); | |
| 745 | } | |
| 746 | ||
| 747 | /* | |
| 748 | * Waste CPU cycles to get in sync with a power line zero crossing. | |
| 749 | * The value returned is roughly how many microseconds we wasted before | |
| 750 | * seeing the transition. To avoid wasting time forever, we give up after | |
| 751 | * waiting patiently for 1/4 sec (15 power line cycles at 60 Hz), | |
| 752 | * which is more than the 11 cycles it takes to transmit a full | |
| 753 | * X-10 packet. | |
| 754 | */ | |
| 755 | ||
| c436375a SW |
756 | static int |
| 757 | wait_for_zero(struct tw_sc *sc) | |
| 984263bc MD |
758 | { |
| 759 | int i, old, new, max; | |
| 760 | int port = sc->sc_port + tw_zcport; | |
| 761 | ||
| 762 | old = sc->sc_xphase; | |
| 763 | max = 10000; /* 10000 * 25us = 0.25 sec */ | |
| 764 | i = 0; | |
| 765 | while(max--) { | |
| 766 | new = inb(port) & tw_zcmask; | |
| 767 | if(new != old) { | |
| 768 | sc->sc_xphase = new; | |
| 769 | return(i*25); | |
| 770 | } | |
| 771 | i++; | |
| 772 | twdelay25(); | |
| 773 | } | |
| 774 | return(-1); | |
| 775 | } | |
| 776 | ||
| 777 | /* | |
| 778 | * Wait for the next zero crossing transition, and if we don't have | |
| 779 | * high-resolution time-of-day, check to see that the zero crossing | |
| 780 | * appears to be arriving on schedule. | |
| 781 | * We expect to be waiting almost a full half-cycle (8.333ms-1ms = 7.333ms). | |
| 782 | * If we don't seem to wait very long, something is wrong (like we got | |
| 783 | * preempted!) and we should abort the transmission because | |
| 784 | * there's no telling how long it's really been since the | |
| 785 | * last bit was transmitted. | |
| 786 | */ | |
| 787 | ||
| c436375a SW |
788 | static int |
| 789 | next_zero(struct tw_sc *sc) | |
| 984263bc MD |
790 | { |
| 791 | int d; | |
| 792 | #ifdef HIRESTIME | |
| 793 | if((d = wait_for_zero(sc)) < 0) { | |
| 794 | #else | |
| 795 | if((d = wait_for_zero(sc)) < 6000 || d > 8500) { | |
| 796 | /* No less than 6.0ms, no more than 8.5ms */ | |
| 797 | #endif /* HIRESTIME */ | |
| 798 | log(LOG_ERR, "TWXMIT framing error: %d\n", d); | |
| 799 | return(-1); | |
| 800 | } | |
| 801 | return(0); | |
| 802 | } | |
| 803 | ||
| 804 | /* | |
| 805 | * Put a three-byte packet into the circular buffer | |
| 8ba87358 | 806 | * Should be called from a critical section. |
| 984263bc MD |
807 | */ |
| 808 | ||
| c436375a SW |
809 | static int |
| 810 | twputpkt(struct tw_sc *sc, u_char *p) | |
| 984263bc MD |
811 | { |
| 812 | int i, next; | |
| 813 | ||
| 814 | for(i = 0; i < 3; i++) { | |
| 815 | next = sc->sc_nextin+1; | |
| 816 | if(next >= TW_SIZE) next = 0; | |
| 817 | if(next == sc->sc_nextout) { /* Buffer full */ | |
| 818 | /* | |
| 819 | log(LOG_ERR, "TWRCV: Buffer overrun\n"); | |
| 820 | */ | |
| 821 | return(1); | |
| 822 | } | |
| 823 | sc->sc_buf[sc->sc_nextin] = *p++; | |
| 824 | sc->sc_nextin = next; | |
| 825 | } | |
| 826 | if(sc->sc_state & TWS_WANT) { | |
| 827 | sc->sc_state &= ~TWS_WANT; | |
| 828 | wakeup((caddr_t)(&sc->sc_buf)); | |
| 829 | } | |
| 830 | selwakeup(&sc->sc_selp); | |
| 831 | return(0); | |
| 832 | } | |
| 833 | ||
| 834 | /* | |
| 835 | * Get bytes from the circular buffer | |
| 8ba87358 | 836 | * Should be called from a critical section. |
| 984263bc MD |
837 | */ |
| 838 | ||
| c436375a SW |
839 | static int |
| 840 | twgetbytes(struct tw_sc *sc, u_char *p, int cnt) | |
| 984263bc MD |
841 | { |
| 842 | int error; | |
| 843 | ||
| 844 | while(cnt--) { | |
| 845 | while(sc->sc_nextin == sc->sc_nextout) { /* Buffer empty */ | |
| 846 | sc->sc_state |= TWS_WANT; | |
| 377d4740 | 847 | error = tsleep((caddr_t)(&sc->sc_buf), PCATCH, "twread", 0); |
| 984263bc MD |
848 | if(error) { |
| 849 | return(error); | |
| 850 | } | |
| 851 | } | |
| 852 | *p++ = sc->sc_buf[sc->sc_nextout++]; | |
| 853 | if(sc->sc_nextout >= TW_SIZE) sc->sc_nextout = 0; | |
| 854 | } | |
| 855 | return(0); | |
| 856 | } | |
| 857 | ||
| 858 | /* | |
| 859 | * Abort reception that has failed to complete in the required time. | |
| 860 | */ | |
| 861 | ||
| 862 | static void | |
| c436375a | 863 | twabortrcv(void *arg) |
| 984263bc MD |
864 | { |
| 865 | struct tw_sc *sc = arg; | |
| 984263bc MD |
866 | u_char pkt[3]; |
| 867 | ||
| 8ba87358 | 868 | crit_enter(); |
| 984263bc MD |
869 | sc->sc_state &= ~TWS_RCVING; |
| 870 | /* simply ignore single isolated interrupts. */ | |
| 871 | if (sc->sc_no_rcv > 1) { | |
| 872 | sc->sc_flags |= TW_RCV_ERROR; | |
| 873 | pkt[0] = sc->sc_flags; | |
| 874 | pkt[1] = pkt[2] = 0; | |
| 875 | twputpkt(sc, pkt); | |
| 876 | log(LOG_ERR, "TWRCV: aborting (%x, %d)\n", sc->sc_bits, sc->sc_rcount); | |
| 877 | twdebugtimes(sc); | |
| 878 | } | |
| 879 | wakeup((caddr_t)sc); | |
| 8ba87358 | 880 | crit_exit(); |
| 984263bc MD |
881 | } |
| 882 | ||
| 883 | static int | |
| 884 | tw_is_within(int value, int expected, int tolerance) | |
| 885 | { | |
| 886 | int diff; | |
| 887 | diff = value - expected; | |
| 888 | if (diff < 0) | |
| 889 | diff *= -1; | |
| 890 | if (diff < tolerance) | |
| 891 | return 1; | |
| 892 | return 0; | |
| 893 | } | |
| 894 | ||
| 895 | /* | |
| 896 | * This routine handles interrupts that occur when there is a falling | |
| 897 | * transition on the RX input. There isn't going to be a transition | |
| 898 | * on every bit (some are zero), but if we are smart and keep track of | |
| 899 | * how long it's been since the last interrupt (via the zero crossing | |
| 900 | * detect line and/or high-resolution time-of-day routine), we can | |
| 901 | * reconstruct the transmission without having to poll. | |
| 902 | */ | |
| 903 | ||
| c436375a SW |
904 | static void |
| 905 | twintr(void *arg) | |
| 984263bc | 906 | { |
| 477d3c1c | 907 | int unit = (int)arg; |
| 984263bc MD |
908 | struct tw_sc *sc = &tw_sc[unit]; |
| 909 | int port; | |
| 910 | int newphase; | |
| 911 | u_char pkt[3]; | |
| 912 | int delay = 0; | |
| 913 | struct timeval tv; | |
| 914 | ||
| 915 | port = sc->sc_port; | |
| 916 | /* | |
| 917 | * Ignore any interrupts that occur if the device is not open. | |
| 918 | */ | |
| 919 | if(sc->sc_state == 0) return; | |
| 920 | newphase = inb(port + tw_zcport) & tw_zcmask; | |
| 921 | microtime(&tv); | |
| 922 | ||
| 923 | /* | |
| 924 | * NEW PACKET: | |
| 925 | * If we aren't currently receiving a packet, set up a new packet | |
| 926 | * and put in the first "1" bit that has just arrived. | |
| 927 | * Arrange for the reception to be aborted if too much time goes by. | |
| 928 | */ | |
| 929 | if((sc->sc_state & TWS_RCVING) == 0) { | |
| 930 | #ifdef HIRESTIME | |
| 931 | twsetuptimes(sc->sc_rtimes); | |
| 932 | #endif /* HIRESTIME */ | |
| 933 | sc->sc_state |= TWS_RCVING; | |
| 934 | sc->sc_rcount = 1; | |
| 935 | if(sc->sc_state & TWS_XMITTING) sc->sc_flags = TW_RCV_LOCAL; | |
| 936 | else sc->sc_flags = 0; | |
| 937 | sc->sc_bits = 0; | |
| 938 | sc->sc_rphase = newphase; | |
| 939 | /* 3 cycles of silence = 3/60 = 1/20 = 50 msec */ | |
| 726b8254 | 940 | callout_reset(&sc->abortrcv_ch, hz / 20, twabortrcv, sc); |
| 984263bc MD |
941 | sc->sc_rcv_time[0] = tv.tv_usec; |
| 942 | sc->sc_no_rcv = 1; | |
| 943 | return; | |
| 944 | } | |
| 726b8254 | 945 | callout_reset(&sc->abortrcv_ch, hz / 20, twabortrcv, sc); |
| 984263bc MD |
946 | newphase = inb(port + tw_zcport) & tw_zcmask; |
| 947 | ||
| 948 | /* enforce a minimum delay since the last interrupt */ | |
| 949 | delay = tv.tv_usec - sc->sc_rcv_time[sc->sc_no_rcv - 1]; | |
| 950 | if (delay < 0) | |
| 951 | delay += 1000000; | |
| 952 | if (delay < TW_MIN_DELAY) | |
| 953 | return; | |
| 954 | ||
| 955 | sc->sc_rcv_time[sc->sc_no_rcv] = tv.tv_usec; | |
| 956 | if (sc->sc_rcv_time[sc->sc_no_rcv] < sc->sc_rcv_time[0]) | |
| 957 | sc->sc_rcv_time[sc->sc_no_rcv] += 1000000; | |
| 958 | sc->sc_no_rcv++; | |
| 959 | ||
| 960 | /* | |
| 961 | * START CODE: | |
| 962 | * The second and third bits are a special case. | |
| 963 | */ | |
| 964 | if (sc->sc_rcount < 3) { | |
| 965 | if ( | |
| 966 | #ifdef HIRESTIME | |
| 967 | tw_is_within(delay, HALFCYCLE, HALFCYCLE / 6) | |
| 968 | #else | |
| 969 | newphase != sc->sc_rphase | |
| 970 | #endif | |
| 971 | ) { | |
| 972 | sc->sc_rcount++; | |
| 973 | } else { | |
| 974 | /* | |
| 975 | * Invalid start code -- abort reception. | |
| 976 | */ | |
| 977 | sc->sc_state &= ~TWS_RCVING; | |
| 978 | sc->sc_flags |= TW_RCV_ERROR; | |
| 726b8254 | 979 | callout_stop(&sc->abortrcv_ch); |
| 984263bc MD |
980 | log(LOG_ERR, "TWRCV: Invalid start code\n"); |
| 981 | twdebugtimes(sc); | |
| 982 | sc->sc_no_rcv = 0; | |
| 983 | return; | |
| 984 | } | |
| 985 | if(sc->sc_rcount == 3) { | |
| 986 | /* | |
| 987 | * We've gotten three "1" bits in a row. The start code | |
| 988 | * is really 1110, but this might be followed by a zero | |
| 989 | * bit from the house code, so if we wait any longer we | |
| 990 | * might be confused about the first house code bit. | |
| 991 | * So, we guess that the start code is correct and insert | |
| 992 | * the trailing zero without actually having seen it. | |
| 993 | * We don't change sc_rphase in this case, because two | |
| 994 | * bit arrivals in a row preserve parity. | |
| 995 | */ | |
| 996 | sc->sc_rcount++; | |
| 997 | return; | |
| 998 | } | |
| 999 | /* | |
| 1000 | * Update sc_rphase to the current phase before returning. | |
| 1001 | */ | |
| 1002 | sc->sc_rphase = newphase; | |
| 1003 | return; | |
| 1004 | } | |
| 1005 | /* | |
| 1006 | * GENERAL CASE: | |
| 1007 | * Now figure out what the current bit is that just arrived. | |
| 1008 | * The X-10 protocol transmits each data bit twice: once in | |
| 1009 | * true form and once in complemented form on the next half | |
| 1010 | * cycle. So, there will be at least one interrupt per bit. | |
| 1011 | * By comparing the phase we see at the time of the interrupt | |
| 1012 | * with the saved sc_rphase, we can tell on which half cycle | |
| 1013 | * the interrupt occrred. This assumes, of course, that the | |
| 1014 | * packet is well-formed. We do the best we can at trying to | |
| 1015 | * catch errors by aborting if too much time has gone by, and | |
| 1016 | * by tossing out a packet if too many bits arrive, but the | |
| 1017 | * whole scheme is probably not as robust as if we had a nice | |
| 1018 | * interrupt on every half cycle of the power line. | |
| 1019 | * If we have high-resolution time-of-day routines, then we | |
| 1020 | * can do a bit more sanity checking. | |
| 1021 | */ | |
| 1022 | ||
| 1023 | /* | |
| 1024 | * A complete packet is 22 half cycles. | |
| 1025 | */ | |
| 1026 | if(sc->sc_rcount <= 20) { | |
| 1027 | #ifdef HIRESTIME | |
| 1028 | int bit = 0, last_bit; | |
| 1029 | if (sc->sc_rcount == 4) | |
| 1030 | last_bit = 1; /* Start (1110) ends in 10, a 'one' code. */ | |
| 1031 | else | |
| 1032 | last_bit = sc->sc_bits & 0x1; | |
| 1033 | if ( ( (last_bit == 1) | |
| 1034 | && (tw_is_within(delay, HALFCYCLE * 2, HALFCYCLE / 6))) | |
| 1035 | || ( (last_bit == 0) | |
| 1036 | && (tw_is_within(delay, HALFCYCLE * 1, HALFCYCLE / 6)))) | |
| 1037 | bit = 1; | |
| 1038 | else if ( ( (last_bit == 1) | |
| 1039 | && (tw_is_within(delay, HALFCYCLE * 3, HALFCYCLE / 6))) | |
| 1040 | || ( (last_bit == 0) | |
| 1041 | && (tw_is_within(delay, HALFCYCLE * 2, HALFCYCLE / 6)))) | |
| 1042 | bit = 0; | |
| 1043 | else { | |
| 1044 | sc->sc_flags |= TW_RCV_ERROR; | |
| 1045 | log(LOG_ERR, "TWRCV: %d cycle after %d bit, delay %d%%\n", | |
| 1046 | sc->sc_rcount, last_bit, 100 * delay / HALFCYCLE); | |
| 1047 | } | |
| 1048 | sc->sc_bits = (sc->sc_bits << 1) | bit; | |
| 1049 | #else | |
| 1050 | sc->sc_bits = (sc->sc_bits << 1) | |
| 1051 | | ((newphase == sc->sc_rphase) ? 0x0 : 0x1); | |
| 1052 | #endif /* HIRESTIME */ | |
| 1053 | sc->sc_rcount += 2; | |
| 1054 | } | |
| 1055 | if(sc->sc_rcount >= 22 || sc->sc_flags & TW_RCV_ERROR) { | |
| 1056 | if(sc->sc_rcount != 22) { | |
| 1057 | sc->sc_flags |= TW_RCV_ERROR; | |
| 1058 | pkt[0] = sc->sc_flags; | |
| 1059 | pkt[1] = pkt[2] = 0; | |
| 1060 | } else { | |
| 1061 | pkt[0] = sc->sc_flags; | |
| 1062 | pkt[1] = X10_HOUSE_INV[(sc->sc_bits & 0x1e0) >> 5]; | |
| 1063 | pkt[2] = X10_KEY_INV[sc->sc_bits & 0x1f]; | |
| 1064 | } | |
| 1065 | sc->sc_state &= ~TWS_RCVING; | |
| 1066 | twputpkt(sc, pkt); | |
| 726b8254 | 1067 | callout_stop(&sc->abortrcv_ch); |
| 984263bc MD |
1068 | if(sc->sc_flags & TW_RCV_ERROR) { |
| 1069 | log(LOG_ERR, "TWRCV: invalid packet: (%d, %x) %c %s\n", | |
| 1070 | sc->sc_rcount, sc->sc_bits, 'A' + pkt[1], X10_KEY_LABEL[pkt[2]]); | |
| 1071 | twdebugtimes(sc); | |
| 1072 | } else { | |
| 1073 | /* log(LOG_ERR, "TWRCV: valid packet: (%d, %x) %c %s\n", | |
| 1074 | sc->sc_rcount, sc->sc_bits, 'A' + pkt[1], X10_KEY_LABEL[pkt[2]]); */ | |
| 1075 | } | |
| 1076 | sc->sc_rcount = 0; | |
| 1077 | wakeup((caddr_t)sc); | |
| 1078 | } | |
| 1079 | } | |
| 1080 | ||
| c436375a SW |
1081 | static void |
| 1082 | twdebugtimes(struct tw_sc *sc) | |
| 984263bc MD |
1083 | { |
| 1084 | int i; | |
| 1085 | for (i = 0; (i < sc->sc_no_rcv) && (i < SC_RCV_TIME_LEN); i++) | |
| 1086 | log(LOG_ERR, "TWRCV: interrupt %2d: %d\t%d%%\n", i, sc->sc_rcv_time[i], | |
| 1087 | (sc->sc_rcv_time[i] - sc->sc_rcv_time[(i?i-1:0)])*100/HALFCYCLE); | |
| 1088 | } | |
| 1089 | ||
| 1090 | #ifdef HIRESTIME | |
| 1091 | /* | |
| 1092 | * Initialize an array of 22 times, starting from the current | |
| 1093 | * microtime and continuing for the next 21 half cycles. | |
| 1094 | * We use the times as a reference to make sure transmission | |
| 1095 | * or reception is on schedule. | |
| 1096 | */ | |
| 1097 | ||
| c436375a SW |
1098 | static void |
| 1099 | twsetuptimes(int *a) | |
| 984263bc MD |
1100 | { |
| 1101 | struct timeval tv; | |
| 1102 | int i, t; | |
| 1103 | ||
| 1104 | microtime(&tv); | |
| 1105 | t = tv.tv_usec; | |
| 1106 | for(i = 0; i < 22; i++) { | |
| 1107 | *a++ = t; | |
| 1108 | t += HALFCYCLE; | |
| 1109 | if(t >= 1000000) t -= 1000000; | |
| 1110 | } | |
| 1111 | } | |
| 1112 | ||
| 1113 | /* | |
| 1114 | * Check the current time against a slot in a previously set up | |
| 1115 | * timing array, and make sure that it looks like we are still | |
| 1116 | * on schedule. | |
| 1117 | */ | |
| 1118 | ||
| c436375a SW |
1119 | static int |
| 1120 | twchecktime(int target, int tol) | |
| 984263bc MD |
1121 | { |
| 1122 | struct timeval tv; | |
| 1123 | int t, d; | |
| 1124 | ||
| 1125 | microtime(&tv); | |
| 1126 | t = tv.tv_usec; | |
| 1127 | d = (target - t) >= 0 ? (target - t) : (t - target); | |
| 1128 | if(d > 500000) d = 1000000-d; | |
| 1129 | if(d <= tol && d >= -tol) { | |
| 1130 | return(1); | |
| 1131 | } else { | |
| 1132 | return(0); | |
| 1133 | } | |
| 1134 | } | |
| 1135 | #endif /* HIRESTIME */ |