1 /*----------------------------------------------------------------------------+
2 | |
3 | HEYU CM10 "IBM Home Director HD16" Support |
4 | Copyright 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 #include <stdio.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include "x10.h"
40 #include "process.h"
41
42 #ifdef SCO
43 #define _SVID3 /* required for correct termio handling */
44 #undef _IBCS2 /* conflicts with SVID3 */
45 #endif
46
47 #include <time.h>
48 #include <unistd.h>
49 #include <signal.h>
50
51 #ifdef LINUX
52 #include <asm/ioctls.h>
53 # ifdef OLDLINUX
54 #include <linux/serial_reg.h>
55 # endif
56 #include <linux/serial.h>
57 #include <sys/ioctl.h>
58 #include <unistd.h>
59 #include <syslog.h>
60 #else
61 # if (defined(DARWIN) || defined(FREEBSD))
62 #include <termios.h>
63 # else
64 # ifdef SCO
65 #include <sys/termio.h>
66 # else
67 # if (defined(POSIX) || defined(OPENBSD))
68 #include <sys/termios.h>
69 # else
70 #include <termio.h>
71 # endif
72 # endif
73 # endif
74 #endif
75
76 #if (defined(OSF) || defined(DARWIN) || defined(NETBSD))
77 #include <sys/ioctl.h>
78 #endif
79
80 #ifdef HASSELECT
81 #include <sys/time.h>
82 #include <sys/types.h>
83 #endif
84
85
86 extern int verbose;
87 extern int sptty;
88 extern int tty;
89 extern int ttylock(), munlock();
90 extern int i_am_relay;
91 extern int xwrite(), exread();
92 extern int check4poll();
93
94 extern CONFIG config;
95 extern CONFIG *configp;
96
97 /* On CM10A versions having a 6-pin RJ11 connector and cable, */
98 /* if the serial port RTS line is turned off for more than about 20 */
99 /* milliseconds, then turned on again, a CM10A will write a bunch */
100 /* of characters to the serial port. This is what I'm seeing with */
101 /* the unit I have. Their interpretation is currently unknown. */
102
103 unsigned char cm10a_standard_response[29] =
104 {0x00,0x78,0x1e,0x00,0x00,0x80,0x78,0x00,
105 0xf0,0xf8,0x00,0x78,0xfe,0x80,0x80,0x00,
106 0x78,0x00,0x00,0x78,0x00,0x00,0x78,0x1e,
107 0x80,0x78,0x1e,0x78,0x1e};
108
109 /* Example CM10 macro from protocol, equivalent to: */
110 /* trigger C1 off mac1 */
111 /* macro mac1 0 dim A1-3 11; on A4 */
112 /* trigger C2 on mac2 */
113 /* macro mac2 0 on A1; dim A2,3 6; off A4 */
114
115 static unsigned char example[] =
116 {0xfb, 0x26,0x0a,0x04,0x66,0x2e,0x04,0x0b,0x02,0x6a,0x02,
117 0x26,0x8c,0x02,0x66,0x02,0x03,0x6e,0x42,0x06,0x02,0x6a,0x03,};
118
119
120 /*----------------------------------------------------------------------------+
121 | Turn the RTS serial line off. |
122 +----------------------------------------------------------------------------*/
turn_rts_off(void)123 int turn_rts_off( void )
124 {
125 int status, retcode;
126
127 retcode = ioctl(tty, TIOCMGET, &status);
128 status &= ~TIOCM_RTS;
129 retcode |= ioctl(tty, TIOCMSET, &status);
130
131 return retcode;
132 }
133
c_turn_rts_off(int argc,char * argv[])134 int c_turn_rts_off( int argc, char *argv[] )
135 {
136 if ( turn_rts_off() != 0 ) {
137 fprintf(stderr, "Unable to turn RTS line Off.\n");
138 return 1;
139 }
140 return 0;
141 }
142
143
144 /*----------------------------------------------------------------------------+
145 | Turn the RTS serial line on. |
146 +----------------------------------------------------------------------------*/
turn_rts_on(void)147 int turn_rts_on( void )
148 {
149 int status, retcode;
150
151 retcode = ioctl(tty, TIOCMGET, &status);
152 status |= TIOCM_RTS;
153 retcode |= ioctl(tty, TIOCMSET, &status);
154
155 return retcode;
156 }
157
c_turn_rts_on(int argc,char * argv[])158 int c_turn_rts_on( int argc, char *argv[] )
159 {
160 if ( turn_rts_on() != 0 ) {
161 fprintf(stderr, "Unable to turn RTS line On.\n");
162 return 1;
163 }
164 return 0;
165 }
166
167
168 /*----------------------------------------------------------------------------+
169 | Toggle the RTS serial line off, then back on |
170 +----------------------------------------------------------------------------*/
toggle_rts(void)171 int toggle_rts( void )
172 {
173 int retcode;
174
175 retcode = turn_rts_off();
176
177 millisleep(100);
178
179 retcode |= turn_rts_on();
180
181 return retcode;
182 }
183
184 /*----------------------------------------------------------------------------+
185 | Ask the CM10A to identify itself. |
186 +----------------------------------------------------------------------------*/
c_cm10a_ident(int argc,char * argv[])187 int c_cm10a_ident ( int argc, char *argv[] )
188 {
189 unsigned char buf[50];
190 unsigned char *bp;
191 int j, count, nread, left;
192 extern void millisleep();
193
194 if ( (toggle_rts()) != 0 ) {
195 fprintf(stderr, "Unable to toggle RTS line.\n");
196 return 1;
197 }
198
199 bp = buf;
200 nread = 0; left = 30;
201 for ( j = 0; j < 3; j++ ) {
202 count = exread(sptty, bp, left, 1);
203 nread += count;
204 if ( nread == 29 ) {
205 if ( memcmp(buf, cm10a_standard_response, 29) == 0 ) {
206 printf("CM10 is connected.\n");
207 check4poll(0,1);
208 return 0;
209 }
210 else {
211 fprintf(stderr, "Non-standard CM10A response.\n");
212 check4poll(0,1);
213 return 1;
214 }
215 }
216 bp += count;
217 left -= count;
218 millisleep(10);
219 }
220
221 if ( nread == 0 ) {
222 fprintf(stderr, "No response from CM10A\n");
223 }
224 else {
225 fprintf(stderr, "Invalid response, %d bytes returned.\n", nread);
226 }
227
228 return 1;
229 }
230
231 /*----------------------------------------------------------------------------+
232 | For versions of the CM10A with a 6-pin RJ11 connector, the factory |
233 | CM10A cable has a jumper between DB-9 pins 4 (DTR) and 6 (DSR), |
234 | neither of which are connected to the interface. These function verify |
235 | that the cable is connected at the PC end at least. |
236 +----------------------------------------------------------------------------*/
237 /*----------------------------------------------------------------------------+
238 | This function just reads the serial status, normally assuming that the DTR |
239 | line is active. It returns 0 if both the DTR and DSR lines are active; |
240 | 1 if the DTR is active and the DSR is not; -1 if the DTR is not. |
241 +----------------------------------------------------------------------------*/
cm10a_cable_check(void)242 int cm10a_cable_check ( void )
243 {
244 int status;
245
246 ioctl(tty, TIOCMGET, &status);
247
248 if ( !(status & TIOCM_DTR) )
249 return -1;
250
251 if ( !(status & TIOCM_DSR) )
252 return 1;
253
254 return 0;
255 }
256
257 /*----------------------------------------------------------------------------+
258 | This function toggles the DTS line. |
259 | It returns 0 if the DSR activity follows the DTR activity. |
260 | Otherwise it returns 1 if DSR remains inactive when the DTR is toggled or |
261 | -1 if the DSR remains active regardless. |
262 +----------------------------------------------------------------------------*/
cm10a_cable_check_full(void)263 int cm10a_cable_check_full ( void )
264 {
265 int status, savestatus;
266
267 ioctl(tty, TIOCMGET, &status);
268 savestatus = status;
269
270 status &= ~TIOCM_DTR;
271 ioctl(tty, TIOCMSET, &status);
272 millisleep(10);
273 ioctl(tty, TIOCMGET, &status);
274 if ( status & TIOCM_DSR ) {
275 ioctl(tty, TIOCMSET, &savestatus);
276 return -1;
277 }
278
279 status |= TIOCM_DTR;
280 ioctl(tty, TIOCMSET, &status);
281 millisleep(10);
282 ioctl(tty, TIOCMGET, &status);
283 if ( !(status & TIOCM_DSR) ) {
284 ioctl(tty, TIOCMSET, &savestatus);
285 return 1;
286 }
287
288 ioctl(tty, TIOCMSET, &savestatus);
289
290 return 0;
291 }
292
293
294 /*----------------------------------------------------------------------------+
295 | Initialize CM10A interface by uploading a dummy macro block. |
296 +----------------------------------------------------------------------------*/
c_cm10a_init(int argc,char * argv[])297 int c_cm10a_init ( int argc, char *argv[] )
298 {
299 unsigned char macrodata[50];
300 unsigned char buf[3];
301 unsigned int j, n, code;
302 unsigned char cksum;
303 extern int usage(), xwrite(), xread(), exread(), check4poll();
304
305 int ignoret;
306
307 if ( !(configp->device_type & DEV_CM10A) && i_am_relay != 1 ) {
308 fprintf(stderr,
309 "Heyu not configured for CM10A - see man page heyu(1)\n");
310 return 1;
311 }
312
313 if( argc > 2 )
314 usage(E_2MANY);
315
316 #if 0
317 if ( cm10a_cable_check() == 1 ) {
318 if ( i_am_relay != 1 )
319 fprintf(stderr, "CM10A is not connected.\n");
320 return 1;
321 }
322 #endif
323
324 macrodata[0] = 0xfb; /* CM10A init code */
325 for ( j = 1; j < 43; j++ ) {
326 macrodata[j] = 0;
327 }
328
329 example[0] = example[0]; /* Keep compiler happy */
330
331 /* For testing, use example CM10 macro data from protocol */
332 #if 0
333 memcpy(macrodata, example, sizeof(example));
334 #endif
335
336
337 cksum = checksum(macrodata + 1, 42);
338
339 code = 0;
340
341 #if 0
342 xwrite(tty, (char *) macrodata, 43);
343 if ( i_am_relay )
344 n = xread(tty, buf, 1, 1);
345 else
346 n = exread(sptty, buf, 1, 1);
347
348 if ( n != 1 )
349 code |= 0x10;
350 else if ( buf[0] != cksum )
351 code |= 0x01;
352
353 xwrite(tty, "\0", 1); /* WRMI */
354 if ( i_am_relay )
355 n = xread(tty, buf, 1, 1);
356 else
357 n = exread(sptty, buf, 1, 1);
358 #endif
359
360 if ( i_am_relay ) {
361 ignoret = write(tty, (char *) macrodata, 43);
362 n = xread(tty, buf, 1, 2);
363 }
364 else {
365 xwrite(tty, (char *) macrodata, 43);
366 n = exread(sptty, buf, 1, 1);
367 }
368
369 if ( n != 1 )
370 code |= 0x10;
371 else if ( buf[0] != cksum )
372 code |= 0x01;
373
374 if ( i_am_relay ) {
375 ignoret = write(tty, "\0", 1); /* WRMI */
376 n = xread(tty, buf, 1, 2);
377 }
378 else {
379 xwrite(tty, "\0", 1); /* WRMI */
380 n = exread(sptty, buf, 1, 1);
381 }
382
383 if ( n != 1 )
384 code |= 0x20;
385 else if ( buf[0] != 0x55 )
386 code |= 0x02;
387
388 if ( argc == 2 )
389 check4poll(0,1); /* zero means to discard data */
390
391 if ( i_am_relay != 1 )
392 (void) printf("CM10A initialized.\n");
393
394 if ( i_am_relay )
395 return code;
396
397 return 0;
398 }
399
400
c_test_serial_port(int argc,char * argv[])401 int c_test_serial_port ( int argc, char *argv[] )
402 {
403 int j, k, nlist, retcode;
404 int all_lines, status, savestatus, result1, result2;
405 int delay = 10;
406
407 static struct tiocm_list {
408 int line;
409 char *name;
410 } list[] = {
411 {TIOCM_LE, "LE" },
412 {TIOCM_DTR, "DTR"},
413 {TIOCM_RTS, "RTS"},
414 {TIOCM_ST, "ST" },
415 {TIOCM_SR, "SR" },
416 {TIOCM_CTS, "CTS"},
417 {TIOCM_CD, "CD" },
418 {TIOCM_RI, "RI" },
419 {TIOCM_DSR, "DSR"},
420 };
421 nlist = (sizeof(list)/sizeof(struct tiocm_list));
422
423 all_lines = 0;
424 for ( j = 0; j < nlist; j++ )
425 all_lines |= list[j].line;
426
427 ioctl(tty, TIOCMGET, &status);
428 savestatus = status;
429
430 if ( status & TIOCM_CTS ) {
431 printf("CTS initially On\n");
432 }
433 else {
434 printf("CTS initially Off\n");
435 }
436
437 status |= TIOCM_CTS;
438 retcode = ioctl(tty, TIOCMBIS, &status);
439 millisleep(delay);
440 status |= TIOCM_CTS;
441 retcode |= ioctl(tty, TIOCMSET, &status);
442 millisleep(delay);
443 retcode |= ioctl(tty, TIOCMGET, &status);
444
445 if ( status & TIOCM_CTS ) {
446 printf("CTS finally On\n");
447 }
448 else {
449 printf("CTS finally Off\n");
450 }
451
452 printf("retcode = %x\n", retcode);
453
454 /* Test individual lines for toggleability */
455 for ( j = 0; j < nlist; j++ ) {
456 retcode = 0;
457 status &= ~list[j].line;
458 retcode |= ioctl(tty, TIOCMSET, &status);
459 if ( delay )
460 millisleep(delay);
461 retcode |= ioctl(tty, TIOCMGET, &status);
462 result1 = status & list[j].line;
463 status |= list[j].line;
464 retcode |= ioctl(tty, TIOCMSET, &status);
465 if ( delay )
466 millisleep(delay);
467 retcode |= ioctl(tty, TIOCMGET, &status);
468 result2 = status & list[j].line;
469 if ( retcode != 0 )
470 printf("Bad retcode\n");
471 if ( result2 != result1 )
472 printf("%-3s can be controlled by PC.\n", list[j].name);
473 }
474
475 /* Test for linked lines */
476 for ( j = 0; j < nlist; j++ ) {
477 retcode = 0;
478 status &= ~all_lines;
479 retcode |= ioctl(tty, TIOCMSET, &status);
480 if ( delay )
481 millisleep(delay);
482 retcode |= ioctl(tty, TIOCMGET, &status);
483 result1 = status & all_lines;
484 status &= ~all_lines;
485 status |= list[j].line;
486 retcode |= ioctl(tty, TIOCMSET, &status);
487 if ( delay )
488 millisleep(delay);
489 retcode |= ioctl(tty, TIOCMGET, &status);
490 result2 = (status ^ result1) & ~list[j].line & all_lines;
491 if ( retcode != 0 )
492 printf("Bad retcode\n");
493 for ( k = 0; k < nlist; k++ ) {
494 if ( result2 & list[k].line )
495 printf("%-3s is linked to %-3s\n", list[k].name, list[j].name);
496 }
497 }
498 for ( j = 0; j < nlist; j++ ) {
499 retcode = 0;
500 status |= all_lines;
501 retcode |= ioctl(tty, TIOCMSET, &status);
502 if ( delay )
503 millisleep(delay);
504 retcode |= ioctl(tty, TIOCMGET, &status);
505 result1 = status & all_lines;
506 status |= all_lines;
507 status &= ~list[j].line;
508 retcode |= ioctl(tty, TIOCMSET, &status);
509 if ( delay )
510 millisleep(delay);
511 retcode |= ioctl(tty, TIOCMGET, &status);
512 result2 = (status ^ result1) & ~list[j].line & all_lines;
513 if ( retcode != 0 )
514 printf("Bad retcode\n");
515 for ( k = 0; k < nlist; k++ ) {
516 if ( result2 & list[k].line )
517 printf("%-3s is revlinked to %-3s\n", list[k].name, list[j].name);
518 }
519 }
520
521 ioctl(tty, TIOCMSET, &savestatus);
522
523 return 0;
524 }
525
526
527
528
529
530