1 /*----------------------------------------------------------------------------+
2 | |
3 | HEYU CM17A "Firecracker" Support |
4 | Copyright 2005, 2006 Charles W. Sullivan |
5 | |
6 | |
7 | As used herein, HEYU is a trademark of Daniel B. Suthers. |
8 | X10, CM11A, and ActiveHome are trademarks of X-10 (USA) Inc. |
9 | The author is not affiliated with either entity. |
10 | |
11 | Charles W. Sullivan |
12 | Co-author and Maintainer |
13 | Greensboro, North Carolina |
14 | Email ID: cwsulliv01 |
15 | Email domain: -at- heyu -dot- org |
16 | |
17 +----------------------------------------------------------------------------*/
18
19 /*
20 * This program is free software: you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation, either version 3 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 *
33 */
34
35 #ifdef HASCM17A /* Compile only if configured for CM17A */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include "x10.h"
42 #include "process.h"
43
44 #ifdef SCO
45 #define _SVID3 /* required for correct termio handling */
46 #undef _IBCS2 /* conflicts with SVID3 */
47 #endif
48
49 #ifdef ATTSVR4
50 #include <sys/time.h>
51 #endif
52
53 #include <time.h>
54 #include <unistd.h>
55 #include <signal.h>
56
57 #ifdef LINUX
58 #include <asm/ioctls.h>
59 # ifdef OLDLINUX
60 #include <linux/serial_reg.h>
61 # endif
62 #include <linux/serial.h>
63 #include <sys/ioctl.h>
64 #include <unistd.h>
65 #include <syslog.h>
66 #else
67 # if (defined(POSIX) || defined(FREEBSD) || defined(OPENBSD))
68 #include <sys/ttycom.h>
69 # else
70 # ifdef SCO
71 #include <sys/ttycom.h>
72 # else
73 # ifdef DARWIN
74 #include <sys/ttycom.h>
75 # else
76 #include <sys/ttycom.h>
77 # endif
78 # endif
79 # endif
80 #endif
81
82 #if (defined(OSF) || defined(DARWIN) || defined(NETBSD))
83 #include <sys/ioctl.h>
84 #endif
85
86 #ifdef HASSELECT
87 #include <sys/time.h>
88 #include <sys/types.h>
89 #endif
90
91
92 extern int verbose;
93 extern int sptty;
94 extern int tty;
95 extern int ttylock(), munlock();
96 extern int i_am_relay;
97
98 extern CONFIG config;
99 extern CONFIG *configp;
100
101 #define FC_STANDBY (TIOCM_RTS | TIOCM_DTR)
102 #define FC_RESET ~FC_STANDBY
103 #define FC_ONE TIOCM_RTS
104 #define FC_ZERO TIOCM_DTR
105
106 #define HC_MASK 0xF000
107 #define FUNC_MASK 0x00B8
108 #define UNIT_MASK 0x0458
109 #define NOSW_MASK 0x0003
110 #define OLD_MASK 0x0800
111 #define NOTX10_MASK ~(HC_MASK|FUNC_MASK|UNIT_MASK|NOSW_MASK|OLD_MASK)
112
113 /* The CM17A will normally repeat its RF code */
114 /* transmission 5 times (configp->def_rf_bursts) */
115 /* at intervals of about 110 milliseconds */
116 /* (configp->rf_burst_spacing). */
117 /* By resetting the unit in one of the intervals */
118 /* between transmissions the number of bursts */
119 /* can be set from 1 through 5. */
120 /* It appears there is an effective window of */
121 /* about 40-50 milliseconds at the end of an */
122 /* interval when the reset will cut off */
123 /* subsequent bursts without interfering with */
124 /* the current burst. */
125 /* Time the reset at OFFSET milliseconds before */
126 /* the start of the next interval. */
127
128 #define OFFSET 10
129
130 /* On many systems the nanosleep() function will */
131 /* have a minimum resolution of 10 milliseconds */
132 /* and a minimum time of 20 milliseconds. */
133 /* The tweak (configp->rf_timer_tweak) attempts to */
134 /* compensate for this to keep within the */
135 /* effective window. */
136
137 /* RF Codes for X10 Housecodes (A-P) */
138 unsigned int hc2rfcode[16] = {
139 0x6000, 0x7000, 0x4000, 0x5000,
140 0x8000, 0x9000, 0xA000, 0xB000,
141 0xE000, 0xF000, 0xC000, 0xD000,
142 0x0000, 0x1000, 0x2000, 0x3000,
143 };
144
145 /* Housecode for RF code (upper nybble) */
146 static char *rf2hc = "MNOPCDABEFGHKLIJ";
147
148 /* RF Codes for X10 _encoded_ unit codes */
149 unsigned int rf_unit_code[16] = {
150 0x0440, 0x0040, 0x0008, 0x0408,
151 0x0448, 0x0048, 0x0000, 0x0400,
152 0x0450, 0x0050, 0x0018, 0x0418,
153 0x0458, 0x0058, 0x0010, 0x0410,
154 };
155
156 /* RF Function names for monitor */
157 char *rf_func_name[] = {
158 "fAllOff", "fLightsOn", "fOn", "fOff",
159 "fDim", "fBright", "fLightsOff",
160 "fdimbo", "freset", "fArb"
161 };
162
163 /* RF Function Codes: */
164 /* AllOff, LightsOn, On, Off, */
165 /* Dim, Bright, LightsOff */
166 unsigned int rf_func_code[] = {
167 0x0080, 0x0090, 0x0000, 0x0020,
168 0x0098, 0x0088, 0x00a0,
169 0x0000, 0x0000, 0x0000
170 };
171
172 /* Undocumented "No Switch" bits for TM751 and RR501 */
173 /* Transceivers. If set, the built-in appliance module */
174 /* relay will not respond to the On, Off, and AllOff RF */
175 /* signals, and will just quietly transceive them. */
176 unsigned int rf_nosw_code[] = {
177 0x0001, 0x0000, 0x0002, 0x0002,
178 0x0000, 0x0000, 0x0000, 0x0000,
179 0x0000, 0x0000,
180 };
181
182 static unsigned long cm17a_loopcount;
183
184 static volatile unsigned long countdown;
185 static volatile int dummy = 1;
186 /*----------------------------------------------------------------------------+
187 | Microsecond timer using timing loop. |
188 +----------------------------------------------------------------------------*/
looptimer(void)189 static void looptimer ( void )
190 {
191 countdown = cm17a_loopcount;
192
193 while (dummy && countdown--);
194 return;
195 }
196
197 /*----------------------------------------------------------------------------+
198 | Microsecond timer using standard timing functions. These don't have |
199 | sufficient resolution for fast RF mode in most Unix-like kernels. |
200 +----------------------------------------------------------------------------*/
stdtimer(void)201 static void stdtimer ( void )
202 {
203 long microsec = configp->cm17a_bit_delay;
204
205 #ifdef NSLEEP
206 struct timestruc_t tspec;
207
208 tspec.tv_sec = microsec / 1000000L;
209 tspec.tv_nsec = 1000L * (microsec % 1000000L);
210
211 while ( nsleep( &tspec, &tspec ) == -1 );
212 return;
213 #else
214 #ifdef ATTSVR4
215 struct timeval tspec;
216
217 tspec.tv_sec = microsec / 1000000;
218 tspec.tv_usec = microsec % 1000000;
219 while ( usleep(tspec.tv_usec) == -1 );
220 #else
221 struct timespec tspec;
222
223 tspec.tv_sec = microsec / 1000000L;
224 tspec.tv_nsec = 1000L * (microsec % 1000000L);
225
226 while ( nanosleep( &tspec, &tspec ) == -1 );
227 #endif /* ATTSVR4 */
228 return;
229 #endif
230 }
231
232
233 /*----------------------------------------------------------------------------+
234 | Delay after normal CM17A command. |
235 +----------------------------------------------------------------------------*/
rf_post_delay(void)236 void rf_post_delay(void)
237 {
238 millisleep(configp->rf_post_delay);
239 return;
240 }
241
242 /*----------------------------------------------------------------------------+
243 | Delay after 'farb' CM17A command |
244 +----------------------------------------------------------------------------*/
rf_farb_delay(void)245 void rf_farb_delay(void)
246 {
247 millisleep(configp->rf_farb_delay);
248 return;
249 }
250
251 /*----------------------------------------------------------------------------+
252 | Delay after 'farw' CM17A command |
253 +----------------------------------------------------------------------------*/
rf_farw_delay(void)254 void rf_farw_delay(void)
255 {
256 millisleep(configp->rf_farw_delay);
257 return;
258 }
259
260 /*----------------------------------------------------------------------------+
261 | Delay after 'flux' CM17A command |
262 +----------------------------------------------------------------------------*/
rf_flux_delay(long msec)263 void rf_flux_delay( long msec )
264 {
265 millisleep(msec);
266 return;
267 }
268
269 /*----------------------------------------------------------------------------+
270 | Reset CM17A to the power-up state - with no log message. |
271 +----------------------------------------------------------------------------*/
reset_cm17a_quiet(void)272 int reset_cm17a_quiet ( void )
273 {
274 int status, retcode;
275
276 retcode = ioctl(tty, TIOCMGET, &status);
277 status &= FC_RESET;
278 retcode |= ioctl(tty, TIOCMSET, &status);
279 millisleep(10);
280
281 retcode = ioctl(tty, TIOCMGET, &status);
282 status = (status & FC_RESET) | FC_STANDBY;
283 retcode |= ioctl(tty, TIOCMSET, &status);
284 millisleep(500);
285
286 return retcode;
287 }
288
289 /*----------------------------------------------------------------------------+
290 | Reset CM17A to the power-up state. |
291 +----------------------------------------------------------------------------*/
reset_cm17a(void)292 int reset_cm17a( void )
293 {
294 send_x10state_command(ST_RESETRF, 0);
295
296 return reset_cm17a_quiet();
297 }
298
299 /*----------------------------------------------------------------------------+
300 | Actuate the CM17A by appropriately toggling the RTS and DTR lines. |
301 +----------------------------------------------------------------------------*/
write_cm17a(unsigned int rfword,int bursts,unsigned char rfmode)302 int write_cm17a( unsigned int rfword, int bursts, unsigned char rfmode )
303 {
304 unsigned char buffer[16];
305 unsigned char data, mask;
306 int status, signal;
307 int i, j, k, retcode, groups, presleep;
308 void (*delay)();
309
310 if ( rfmode == RF_FAST && configp->timer_loopcount > 0 ) {
311 cm17a_loopcount =
312 configp->cm17a_bit_delay * (configp->timer_loopcount / 1000000L);
313 delay = looptimer;
314 }
315 else {
316 delay = stdtimer;
317 }
318
319 if ( rfmode == RF_FAST ) {
320 groups = (bursts - 1) / (int)configp->def_rf_bursts + 1;
321 bursts = (bursts - 1) % (int)configp->def_rf_bursts + 1;
322 presleep = (configp->rf_burst_spacing * configp->def_rf_bursts)
323 - OFFSET - configp->rf_timer_tweak;
324 }
325 else {
326 groups = 1;
327 bursts = min(bursts, (int)configp->def_rf_bursts);
328 presleep = 0;
329 }
330
331 buffer[0] = 0xD5;
332 buffer[1] = 0xAA;
333 buffer[2] = (rfword & 0xff00) >> 8;
334 buffer[3] = rfword & 0xff;
335 buffer[4] = 0xAD;
336
337 retcode = ioctl(tty, TIOCMGET, &status);
338 status |= FC_STANDBY;
339 retcode |= ioctl(tty, TIOCMSET, &status);
340 delay();
341
342 for ( i = 0; i < groups; i++ ) {
343 if ( i > 0 )
344 millisleep( presleep );
345 for ( j = 0; j < 5; j++ ) {
346 data = buffer[j];
347 mask = 0x80;
348 for ( k = 0; k < 8; k++ ) {
349 delay(); /* Put delay at beginning of loop */
350 signal = (data & mask) ? FC_ONE : FC_ZERO ;
351 #if 0
352 status &= FC_RESET;
353 status |= signal;
354 #endif
355 status = (status & FC_RESET) | signal;
356 retcode |= ioctl(tty, TIOCMSET, &status);
357 delay();
358 #if 0
359 status |= FC_STANDBY;
360 #endif
361 status = (status & FC_RESET) | FC_STANDBY;
362 retcode |= ioctl(tty, TIOCMSET, &status);
363 mask = mask >> 1;
364 }
365 }
366 }
367
368 /* Timed reset here limits the number of RF bursts */
369 if ( bursts > 0 ) {
370 millisleep((configp->rf_burst_spacing * bursts)
371 - OFFSET - configp->rf_timer_tweak);
372 status &= FC_RESET;
373 retcode |= ioctl(tty, TIOCMSET, &status);
374 millisleep(10);
375 status |= FC_STANDBY;
376 retcode |= ioctl(tty, TIOCMSET, &status);
377 millisleep(10);
378 }
379
380 return retcode;
381 }
382
383 /*----------------------------------------------------------------------------+
384 |
385 +----------------------------------------------------------------------------*/
xlate_rf(unsigned char type,char ** fname,unsigned int rfword,char * hcp,int * unitp,unsigned char * nosw)386 void xlate_rf( unsigned char type, char **fname, unsigned int rfword,
387 char *hcp, int *unitp, unsigned char *nosw )
388 {
389 int j;
390
391 *fname = rf_func_name[type];
392 if ( type == 9 )
393 return;
394
395 *hcp = rf2hc[(rfword & HC_MASK) >> 12];
396 *nosw = rfword & NOSW_MASK;
397
398 rfword &= UNIT_MASK;
399 *unitp = 0;
400 for ( j = 0; j < 16; j++ ) {
401 if ( rfword == rf_unit_code[j] ) {
402 *unitp = code2unit(j);
403 break;
404 }
405 }
406 return;
407 }
408
409 /*----------------------------------------------------------------------------+
410 | Display the CM17A command in the monitor/logfile |
411 | |
412 | *** This really doesn't work very well, especially with multiple RF |
413 | commands. The uncertain delay between transmission of the RF and the |
414 | reporting of the received power line signal by the CM11A often results in |
415 | the RF signals not properly interleaved with the resulting power line |
416 | signals. It can be corrected only by setting an unreasonably long |
417 | post-delay. |
418 +----------------------------------------------------------------------------*/
display_rf_xmit(unsigned char type,unsigned int rfword,int bursts)419 int display_rf_xmit ( unsigned char type, unsigned int rfword, int bursts )
420 {
421 extern int sptty;
422
423 int ignoret;
424
425 static unsigned char template[10] = {
426 0xff,0xff,0xff,6,ST_COMMAND,ST_XMITRF,0,0,0,0};
427
428 if ( configp->disp_rf_xmit == NO )
429 return 0;
430
431 template[6] = type;
432 template[7] = (rfword & 0xFF00) >> 8;
433 template[8] = rfword & 0xFF;
434 template[9] = (unsigned char)bursts;
435
436 ignoret = write(sptty, template, 10);
437
438 return 0;
439 }
440
441
442 #else /* Stubs */
xlate_rf(unsigned char type,char ** fname,unsigned int rfword,char * hcp,int * unitp,unsigned char * nosw)443 void xlate_rf( unsigned char type, char **fname, unsigned int rfword,
444 char *hcp, int *unitp, unsigned char *nosw ) {}
445 #endif /* End of HASCM17A code */
446
447
448
449