1 /*****************************************************************************/
2 /* */
3 /* ICCOM.CC */
4 /* */
5 /* (C) 1995-97 Ullrich von Bassewitz */
6 /* Wacholderweg 14 */
7 /* D-70597 Stuttgart */
8 /* EMail: uz@ibb.schwaben.com */
9 /* */
10 /*****************************************************************************/
11
12
13
14 // $Id$
15 //
16 // $Log$
17 //
18 //
19
20
21
22 #include "check.h"
23 #include "delay.h"
24 #include "sercom.h"
25 #include "circbuf.h"
26 #include "progutil.h"
27
28 #include "icmsg.h"
29 #include "icevents.h"
30 #include "icconfig.h"
31 #include "istecmsg.h"
32 #include "icdlog.h"
33 #include "icdiag.h"
34 #include "iccli.h"
35 #include "iccti.h"
36 #include "iccom.h"
37
38
39
40 /*****************************************************************************/
41 /* Data */
42 /*****************************************************************************/
43
44
45
46 // Port address and irq used
47 unsigned PortBase = 0;
48 unsigned PortIRQ = 0;
49
50 // Flag for short or long wait after sending a message
51 int ShortWaitAfterMsg = 1;
52
53 // Counter for enabling/disabling diag mode
54 static int DiagModeCounter = 1;
55
56 // Version of the config program (for firmware 2.0 and above)
57 unsigned char ConfigVersionHigh = 2;
58 unsigned char ConfigVersionLow = 0;
59
60 // Current ISTEC charges
61 IstecCharges Charges;
62
63 // Some flags that are used when we receive config or charge messages
64 static int ChargeUpdate = 0;
65 //static int BaseConfigUpdate = 0;
66 //static int DevConfigUpdate = 0;
67
68 // Com port instance
69 static ComPort* Port = NULL;
70
71 // Variables for the read routine
72 static enum {
73 stIdle, // No current message
74 stInBlock, // Currently reading a block
75 stGotBlock, // Got a complete block
76 stInLastBlock, // Currently reading the last block of a msg
77 stFillBytes // Reading the fill bytes of the last block
78 } ICReadStat = stIdle;
79
80 // Buffers and counters
81 static unsigned ICBlockCount = 0;
82 static unsigned ICReadCount = 0;
83 static unsigned ICFillCount = 0;
84 static unsigned ICMsgCount = 0;
85 static unsigned char ICReadBuf [512];
86
87 // The last non diag message received
88 static IstecMsg* LastIstecMsg = NULL;
89
90
91
92 /*****************************************************************************/
93 /* Explicit template instantiation */
94 /*****************************************************************************/
95
96
97
98 #ifdef EXPLICIT_TEMPLATES
99 template class CircularBuffer<IstecMsg*, 64>;
100 #endif
101
102
103
104 /*****************************************************************************/
105 /* Com port related code */
106 /*****************************************************************************/
107
108
109
CloseComPort()110 void CloseComPort ()
111 // Close the com port
112 {
113 if (Port) {
114 if (Port->IsOpen ()) {
115 // Switch off DTR
116 Port->DTROff ();
117 }
118
119 // Delete the port object
120 delete Port;
121 Port = NULL;
122 }
123 }
124
125
126
OpenComPort(const String & PortName)127 int OpenComPort (const String& PortName)
128 // Try to open the com port. If the port is already open, it is closed and
129 // reopened. The function returns 0 on success and an error code on failure.
130 {
131 // Close the port in case it is open
132 CloseComPort ();
133
134 // Ok, now reopen it. Use 9600 8N1 without handshaking
135 Port = new ComPort (PortName, 9600, 8, 'N', 1, 'D', 'D', PortBase, PortIRQ);
136 int Result = Port->Open ();
137 if (Result != 0) {
138 // Error, maybe the device does not exist or is already in use
139 CloseComPort ();
140 return Result;
141 }
142
143 // Set the timeout value for sending and receiving
144 Port->SetRXTimeout (3.0);
145 Port->SetTXTimeout (3.0);
146
147 // Make the RTS line active. This is needed for the new PCB of the istec
148 // (beginning from version #3).
149 Port->RTSOn ();
150
151 // Success
152 return 0;
153 }
154
155
156
ComPortAvail()157 int ComPortAvail ()
158 // Return 1 if the com port is open, 0 if not
159 {
160 return Port != NULL;
161 }
162
163
164
165 /*****************************************************************************/
166 /* Helper functions */
167 /*****************************************************************************/
168
169
170
MapCTIError(unsigned CTIError)171 static int MapCTIError (unsigned CTIError)
172 // Map a CTI error to one of the ieXXX function return codes
173 {
174 switch (CTIError) {
175
176 case CTI_RC_ERROR: return ieCTIError;
177 case CTI_RC_INVALID_NUMBER: return ieCTIError;
178 case CTI_RC_INVALID_SP_STELLE: return ieCTIError;
179 case CTI_RC_INVALID_CHANNEL: return ieCTIError;
180 case CTI_RC_INVALID_DAY_NIGHT: return ieCTIError;
181 case CTI_RC_EEPROM_IN_USE: return ieEEPROMInUse;
182 case CTI_RC_DAY_NIGHT_CHANGED: return ieDone;
183 case CTI_RC_DEFAULT_VALUES: return ieCTIError;
184 case CTI_RC_DAY_NIGHT_SAME: return ieDone;
185 default: return ieCTIError;
186 }
187 }
188
189
190
191 /*****************************************************************************/
192 /* Low level ISTEC specific code */
193 /*****************************************************************************/
194
195
196
UpdateCharges(const IstecMsg * Msg)197 static void UpdateCharges (const IstecMsg* Msg)
198 // Copy the charges
199 {
200 // Unpack the new charges into the static area
201 Charges.Unpack (&Msg->Data [1]);
202
203 // We did receive a charge update
204 ChargeUpdate = 1;
205
206 // Post an appropriate event
207 PostEvent (evChargeUpdate);
208 }
209
210
211
IstecWrite(const unsigned char * Msg,unsigned BufSize)212 static void IstecWrite (const unsigned char* Msg, unsigned BufSize)
213 // Recode the binary message into the istec format and send it via the port.
214 {
215 // A static buffer to hold outgoing messages
216 static CircularBuffer<IstecMsg*, 64> MsgBuf;
217
218 // Check the parameters
219 PRECONDITION (Msg != NULL && BufSize > 0);
220
221 // If the port is unavailable, we have nothing to do
222 if (Port == NULL) {
223 return;
224 }
225
226 // Create a message from the given data
227 IstecMsg* IM = new IstecMsg (BufSize, Msg);
228
229 // Insert the message into the output buffer
230 MsgBuf.Put (IM, 0);
231
232 // Now have a look at the semaphore variable. If it is already set,
233 // someone is currently executing this code and will take care of
234 // our message in the buffer.
235 static int Running = 0;
236 if (Running) {
237 // Someone is alredy executing this routine
238 return;
239 }
240
241 // No one executing, block it
242 Running = 1;
243
244 // Send all message in the buffer
245 while (!MsgBuf.IsEmpty ()) {
246
247 // Get the next message
248 IstecMsg* IM = MsgBuf.Get ();
249
250 // Determine the real message size from the message
251 unsigned Size = SendMsgSize (IM->Data, IM->Size);
252
253 // Log the outgoing message
254 WriteDebugLog ("Outgoing: " + IM->AsciiData ());
255
256 // Get a pointer to the message data
257 unsigned char* Msg = IM->Data;
258
259 // Send the message
260 char Buf [4]; // ## should be unsigned
261 while (Size) {
262
263 // Set up the length of the paket
264 unsigned BytesToSend = Size > 3 ? 3 : Size;
265 Size -= BytesToSend;
266
267 // Time to wait after chunk (the times used here are the times the
268 // ISTEC software uses: 40ms after a chunk and an additional 60ms
269 // [making a total of 100ms] after the last chunk)
270 unsigned DelayTime = 40;
271
272 // Set up the header byte
273 Buf [0] = BytesToSend;
274 if (Size == 0) {
275 // Last chunk, mark it
276 Buf [0] |= 0x80;
277
278 // If ShortWaitAfterMsg is *not* set, use the long delay to
279 // give the istec time to swallow the command.
280 if (!ShortWaitAfterMsg) {
281 DelayTime = 100;
282 }
283 }
284
285 // Copy parameter bytes
286 unsigned I = 0;
287 while (I < BytesToSend) {
288 I++;
289 Buf [I] = *Msg++;
290 }
291
292 // Fill rest with 0x00
293 while (I < sizeof (Buf) - 1) {
294 I++;
295 Buf [I] = '\0';
296 }
297
298 // Send the paket
299 for (I = 0; I < sizeof (Buf); I++) {
300 if (Port->TimedSend (Buf [I]) == -1) {
301 // Timeout
302 WriteDebugLog ("Error: Timeout on outgoing message!");
303 // Try the next message
304 break;
305 }
306 }
307
308 if (DelayTime) {
309 Delay (DelayTime);
310 }
311
312 }
313
314 // Delete the message we've just sent
315 delete IM;
316
317 }
318
319 // All messages sent. Reset the flag
320 Running = 0;
321 }
322
323
324
IstecReadChar(unsigned char C)325 static IstecMsg* IstecReadChar (unsigned char C)
326 // Is called when a character is available on the serial line. According
327 // to the status in ICReadStat, the character is handled. If the received
328 // message is complete, a copy of the message is returned in a buffer allocated
329 // with new.
330 {
331 // Check the status
332 switch (ICReadStat) {
333
334 case stIdle:
335 case stGotBlock:
336 // We got a complete block and are waiting for the header of a
337 // new one. C contains the byte count of the new block.
338 if (C & 0x80) {
339 // This one is the last block
340 ICReadStat = stInLastBlock;
341 ICBlockCount = C & 0x7F;
342 ICFillCount = 3 - ICBlockCount;
343 } else {
344 ICReadStat = stInBlock;
345 ICBlockCount = C;
346 }
347 // If we get a block with an invalid block size, we are in
348 // trouble (this may happen if we are connected to the wrong
349 // port). Ignore the block and go back into idle state hoping
350 // that a timeout will clear things.
351 if (ICBlockCount == 0 || ICBlockCount > 3) {
352 ICReadStat = stIdle;
353 }
354 break;
355
356 case stInBlock:
357 // We are currently reading a block. ICBlockCount contains the
358 // count of outstanding characters for this block. Place the
359 // received character into the receive buffer.
360 if (ICReadCount < sizeof (ICReadBuf)) {
361 ICReadBuf [ICReadCount++] = C;
362 }
363 ICBlockCount--;
364 if (ICBlockCount == 0) {
365 // Got that block
366 ICReadStat = stGotBlock;
367 }
368 break;
369
370 case stInLastBlock:
371 // We are currently reading the last block. ICBlockCount contains
372 // the count of outstanding characters for this block. Place the
373 // received character into the receive buffer.
374 if (ICReadCount < sizeof (ICReadBuf)) {
375 ICReadBuf [ICReadCount++] = C;
376 }
377 ICBlockCount--;
378 if (ICBlockCount == 0) {
379 // Got that block. Receive fill bytes or end
380 if (ICFillCount > 0) {
381 ICReadStat = stFillBytes;
382 } else {
383 // Got a complete message
384 ICReadStat = stIdle;
385 ICMsgCount++;
386 }
387 }
388 break;
389
390 case stFillBytes:
391 // We are reading the fill bytes of the last block. Ignore the
392 // bytes and wait for the end of the message
393 ICFillCount--;
394 if (ICFillCount == 0) {
395 // Got the fill bytes
396 ICReadStat = stIdle;
397 ICMsgCount++;
398 }
399 break;
400
401 default:
402 FAIL ("IstecReadChar: Invalid machine state");
403 break;
404
405 }
406
407 // Check if we did receive a complete message
408 while (ICMsgCount > 0) {
409
410 // Create an istec message from the buffer. Check if there is more than
411 // one message in the buffer (this seems to be a bug in the istec
412 // firmware)
413 // If the proposed length is greater than the amount of bytes read,
414 // we can only use what we have. If the proposed length is smaller,
415 // assume that we received more than one message in a chunk and
416 // use only part of the data.
417 unsigned ProposedLen = RecMsgSize (ICReadBuf, ICReadCount);
418 IstecMsg* IM;
419 if (ProposedLen == ICReadCount) {
420 // We received the amount of bytes, we expected.
421 IM = new IstecMsg (ICReadCount, ICReadBuf);
422 ICReadCount = 0;
423 ICMsgCount = 0;
424 } else if (ICReadCount > ProposedLen) {
425 // We got more bytes than we expected. Assume that there is more
426 // than one message in the chunk. Since we try to handle this
427 // condition silently, we will not set an error code.
428 String Msg = FormatStr ("Error: Message length does not match. "
429 "Expected %d, got %d bytes: ",
430 ProposedLen, ICReadCount);
431 WriteDebugLog (Msg + AsciiData (ICReadBuf, ICReadCount));
432
433 // Create the message
434 IM = new IstecMsg (ProposedLen, ICReadBuf);
435
436 // Delete the bytes we read from the buffer but leave ICMsgCount
437 // untouched since there are bytes left
438 ICReadCount -= ProposedLen;
439 memmove (ICReadBuf, &ICReadBuf [ProposedLen], ICReadCount);
440
441 } else {
442
443 // We got less byte then we expected. This is clearly an error as
444 // we have not enough bytes to handle.
445 String Msg = FormatStr ("Error: Message length does not match. "
446 "Expected %d, got %d bytes: ",
447 ProposedLen, ICReadCount);
448 WriteDebugLog (Msg + AsciiData (ICReadBuf, ICReadCount));
449
450 // Create the message
451 IM = new IstecMsg (ICReadCount, ICReadBuf);
452
453 // Set the error code
454 IM->SetError (ieRecBufUnderflow);
455
456 // Delete the bytes we read from the buffer
457 ICReadCount = 0;
458 ICMsgCount = 0;
459 }
460
461 // Log the received message
462 WriteDebugLog ("Incoming: " + IM->AsciiData ());
463
464 // Look for special messages and handle them directly (don't return
465 // those messages to the caller)
466 if (IM->IsDiagMsg ()) {
467
468 // The message is a diagnostic message
469 HandleDiagMsg (IM->Data);
470 delete IM;
471 IM = NULL;
472
473 } else if (IM->IsCLIMsg ()) {
474
475 // The message is a calling line information
476 HandleCLIMsg (IM->Data, IM->Size);
477 delete IM;
478 IM = NULL;
479
480 } else if (IM->IsChargeInfo ()) {
481
482 // The message is a charge info message
483 UpdateCharges (IM);
484 delete IM;
485 IM = NULL;
486
487 } else {
488
489 // Cannot handle the message - return it
490 return IM;
491
492 }
493
494 }
495
496 // No complete message left, return a NULL pointer
497 return NULL;
498
499 }
500
501
502
IstecRead()503 static IstecMsg* IstecRead ()
504 // Read and return a complete message from the istec.
505 {
506 IstecMsg* IM;
507
508 // Check if there is an already received message
509 if (LastIstecMsg != NULL) {
510
511 // There is an already received message - grab it
512 IM = LastIstecMsg;
513 LastIstecMsg = NULL;
514
515 } else {
516
517 // No message, try to receive one
518 do {
519 // Get a char with timeout
520 int C = Port->TimedReceive ();
521 if (C == -1) {
522 // Timeout
523 IM = new IstecMsg (0);
524 IM->SetError (ieTimeout);
525 return IM;
526 }
527
528 // Handle the char, receive a complete message if available
529 IM = IstecReadChar (C);
530
531 } while (IM == NULL);
532
533 }
534
535 // Return the message.
536 return IM;
537 }
538
539
540
IstecPoll()541 void IstecPoll ()
542 // Poll the istec for incoming diag messages. If we get a real message, store
543 // it in LastIstecMsg (there should be only one outstanding real message at a
544 // time).
545 {
546 // If we don't have a valid port, ignore the call
547 if (Port == NULL) {
548 return;
549 }
550
551 // Handle all characters in the receive queue
552 unsigned Count;
553 while ((Count = Port->RXCount ()) > 0) {
554
555 while (Count--) {
556
557 // Get a char and handle it
558 int C = Port->TimedReceive ();
559 CHECK (C != -1);
560 IstecMsg* IM = IstecReadChar (C);
561
562 // If we have a message, save it into the message buffer
563 if (IM) {
564
565 // This is not a diagnose message. As we have only room to
566 // buffer one message, delete the buffer contents before
567 // storing (can happen only if the ISTEC does not work
568 // correctly, ESTIC should not crash because of that).
569 if (LastIstecMsg) {
570 // OOPS - there is a message already waiting
571 WriteDebugLog ("Error: Overwriting waiting message: " +
572 AsciiData (LastIstecMsg->Data, LastIstecMsg->Size));
573 delete LastIstecMsg;
574 }
575 LastIstecMsg = IM;
576
577 }
578 }
579 }
580 }
581
582
583
IstecReadAck(unsigned char Ack)584 static int IstecReadAck (unsigned char Ack)
585 // Wait for an ack from the istec
586 {
587 // Assume no errors
588
589 // Wait for the acknowledgement
590 IstecMsg* Reply = IstecRead ();
591 int RetCode = Reply->GetError ();
592
593 if (RetCode == ieDone) {
594
595 // We received the message successfully, check the ack code
596 if (Reply->At (0) != Ack) {
597 // OOPS - got wrong answer
598 WriteDebugLog (FormatStr ("Error: Got wrong ACK: Expected 0x%02X, got 0x%02X",
599 Ack, Reply->At (0)));
600 RetCode = ieInvalidReply;
601 }
602
603 }
604
605 // Delete the message and return the result
606 delete Reply;
607 return RetCode;
608 }
609
610
611
612 /*****************************************************************************/
613 /* High level ISTEC specific code */
614 /*****************************************************************************/
615
616
617
IstecErrorSync()618 void IstecErrorSync ()
619 // Try to resync the istec after an error
620 {
621 // First, wait some time. IstecPoll will be called in this time
622 Delay (250);
623
624 // Call IstecPoll to fetch all characters that are currently in the
625 // incoming buffer.
626 IstecPoll ();
627
628 // If we have a waiting message now, throw it away
629 if (LastIstecMsg) {
630 // Log the facts
631 WriteDebugLog ("ErrorSync: Killing waiting message: " +
632 LastIstecMsg->AsciiData ());
633
634 // Delete the message
635 delete LastIstecMsg;
636 LastIstecMsg = NULL;
637
638 }
639 }
640
641
642
IstecReady()643 int IstecReady ()
644 // Check if the istec answers the "Ready" message.
645 {
646 // Old firmware versions expect one byte here, newer version (2.0 and
647 // above) expect three bytes. It seems as if the old versions ignore
648 // additional bytes, so I will send three bytes in any case. This must
649 // be changed if there are problems.
650 unsigned char Msg [3] = { 0x02 };
651
652 // Put the version into bytes 1 and 2
653 Msg [1] = ConfigVersionHigh;
654 Msg [2] = ConfigVersionLow;
655
656 // Send the command to the istec
657 IstecWrite (Msg, sizeof (Msg));
658
659 // Ok, now wait for the reply
660 return IstecReadAck (0x12);
661 }
662
663
664
IstecRequestCharges()665 void IstecRequestCharges ()
666 // Request the device charges from the istec. This function is different from
667 // the "Get" functions as it does not wait for a reply. The charge messages
668 // from the ISTEC are handled by the IstecPoll function in the background.
669 // If new charges are available, they are passed to the function NewChargeInfo
670 // of the application object.
671 {
672 // ISTEC Command
673 static unsigned char Msg [1] = { 0x06 };
674
675 // Send the command to the istec
676 IstecWrite (Msg, sizeof (Msg));
677 }
678
679
680
IstecGetCharges()681 int IstecGetCharges ()
682 // Get the device charges from the istec. This function calls the "Request"
683 // function and waits until a timeout occurs or we get a reply.
684 {
685 // Reset the flag
686 ChargeUpdate = 0;
687
688 // Send the command to the istec
689 IstecRequestCharges ();
690
691 // Wait for the new charges
692 unsigned I = 0;
693 do {
694 Delay (200);
695 } while (++I < 10 && ChargeUpdate == 0);
696
697 // Check for a timeout
698 if (ChargeUpdate == 0) {
699 // Timeout, return an error code
700 return ieTimeout;
701 } else {
702 return ieDone;
703 }
704 }
705
706
707
IstecPutCharges(const IstecCharges & NewCharges)708 void IstecPutCharges (const IstecCharges& NewCharges)
709 // Write the given charges to the istec
710 {
711 // First byte is opcode, charges are parameters
712 unsigned char Buf [ChargeSize + 1];
713 Buf [0] = 0x05;
714 NewCharges.Pack (&Buf [1]);
715
716 // Write it out
717 if (FirmwareVersion < 2.00) {
718 // Old versions write charges for 64 devices
719 IstecWrite (Buf, 1 + 64 * sizeof (u16));
720 } else {
721 // New versions write charges for 8 devices
722 IstecWrite (Buf, 1 + 8 * sizeof (u16));
723 }
724
725 // Use the new charges
726 Charges = NewCharges;
727
728 // Post an appropriate event
729 PostEvent (evChargeUpdate);
730 }
731
732
733
IstecGetDevConfig(IstecConfig & Config)734 static int IstecGetDevConfig (IstecConfig& Config)
735 // Request the device configurations from the istec.
736 {
737 // ISTEC Command
738 static unsigned char Msg [1] = { 0x08 };
739
740 // Send the command to the istec
741 IstecWrite (Msg, sizeof (Msg));
742
743 // Determine how many device infos come from the istec. Note: this depends
744 // on the firmware version!
745 unsigned DevCount = FirmwareVersion < 1.93? IstecDevCount : Config.GetDevCount ();
746
747 // Ok, now we get many replys
748 for (unsigned I = 0; I < DevCount; I++) {
749
750 IstecMsg* Reply = IstecRead ();
751 int Result = Reply->GetError ();
752
753 // If the return code is ok, check the message code
754 if (Result == ieDone) {
755
756 // Check the return message code
757 if (Reply->At (0) != 0x16) {
758 WriteDebugLog ("Error: Got invalid reply on request 0x08: " +
759 Reply->AsciiData ());
760 Result = ieInvalidReply;
761 } else {
762 // Ok, we got the answer from the istec. Copy the data into the config
763 // struct
764 Config.UnpackDevConfig (*Reply);
765 }
766
767 }
768
769 // Delete the message
770 delete Reply;
771
772 // If the return code is not ok, bail out
773 if (Result != ieDone) {
774 return Result;
775 }
776 }
777
778 // Got it
779 return ieDone;
780 }
781
782
783
IstecGetBaseConfig(IstecConfig & Config)784 static int IstecGetBaseConfig (IstecConfig& Config)
785 // Request the basic configuration from the istec.
786 {
787 // ISTEC Command
788 static unsigned char Msg [1] = { 0x0A };
789
790 // Send the command to the istec
791 IstecWrite (Msg, sizeof (Msg));
792
793 // Ok, now wait for the reply
794 IstecMsg* Reply = IstecRead ();
795 int Result = Reply->GetError ();
796 if (Result == ieDone) {
797
798 // Check the return message opcode
799 if (Reply->At (0) != 0x17) {
800 WriteDebugLog ("Error: Got invalid reply on request 0x0A: " +
801 Reply->AsciiData ());
802 Result = ieInvalidReply;
803 } else {
804
805 // Ok, we got the answer from the istec. Copy the data into the config
806 // struct, update the save firmware version from the istec data and
807 // return a success code to the caller
808 Config.UnpackBaseConfig (*Reply);
809 FirmwareVersion = Config.GetFirmwareVersion();
810 }
811 }
812
813 // Delete the message, return the result code
814 delete Reply;
815 return Result;
816 }
817
818
819
IstecGetConfig(IstecConfig & Config)820 int IstecGetConfig (IstecConfig& Config)
821 // Get the complete configuration from the istec
822 {
823 int Result;
824
825 // Read the base configuration
826 if ((Result = IstecGetBaseConfig (Config)) != ieDone) {
827 return Result;
828 }
829
830 // Read the device configurations
831 if ((Result = IstecGetDevConfig (Config)) != ieDone) {
832 return Result;
833 }
834
835 // Ok, all done
836 return ieDone;
837 }
838
839
840
IstecPutDevConfig(const IstecConfig & Config)841 static int IstecPutDevConfig (const IstecConfig& Config)
842 // Write a set of device configuration data to the istec.
843 {
844 unsigned DevCount = Config.GetDevCount ();
845 for (unsigned I = 0; I < DevCount; I++) {
846
847 int Result;
848
849 // Set up the command
850 unsigned char Buf [DevConfigSize + 1];
851 Buf [0] = 0x07;
852 Config.PackDevConfig (I, &Buf [1]);
853
854 // Write it out
855 IstecWrite (Buf, sizeof (Buf));
856
857 // Wait for the reply
858 IstecMsg* Reply = IstecRead ();
859 Result = Reply->GetError ();
860
861 if (Result == ieDone) {
862 // Check the reply
863 if (Reply->At (0) != 0x18) {
864 Result = ieInvalidReply;
865 } else if (Reply->At (1) != I) {
866 // Reply has the wrong device number
867 Result = ieWrongDevice;
868 }
869 }
870
871 // Delete the message
872 delete Reply;
873
874 // Bail out if we had an error
875 if (Result != ieDone) {
876 return Result;
877 }
878 }
879
880 // Ok, all done
881 return ieDone;
882 }
883
884
885
IstecPutBaseConfig(const IstecConfig & Config)886 static void IstecPutBaseConfig (const IstecConfig& Config)
887 // Write a base configuration to the istec
888 {
889 unsigned char Buf [BaseConfigSize + 1];
890
891 // First byte is opcode, base config is parameter
892 Buf [0] = 0x09;
893 Config.PackBaseConfig (&Buf [1]);
894
895 // Write it out
896 IstecWrite (Buf, sizeof (Buf));
897 }
898
899
900
IstecMakePermanent()901 static int IstecMakePermanent ()
902 // Send the command to the istec to store the current configuration into the
903 // EEPROM.
904 {
905 // Send the command
906 static unsigned char Msg [1] = { 0x0C };
907 IstecWrite (Msg, sizeof (Msg));
908
909 // Wait for the acknowledgement
910 return IstecReadAck (0x11);
911 }
912
913
914
IstecPutConfig(const IstecConfig & Config)915 int IstecPutConfig (const IstecConfig& Config)
916 // Write the complete configuration to the istec and make it permanent. To
917 // make things short, the number of devices is given as an parameter
918 {
919 int Result;
920
921 // According to Norbert Richter, the firmware version 2.0 needs an
922 // "are you there" request *immidiately* before storing the base
923 // configuration, otherwise data that is present only in version 2.0
924 // will not be stored in EEPROM.
925 if (FirmwareVersion >= 2.0) {
926
927 if ((Result = IstecReady ()) != ieDone) {
928 return Result;
929 }
930
931 }
932
933 // Write the base configuration (there's no return code for this one)
934 IstecPutBaseConfig (Config);
935
936 // Write the device configurations
937 if ((Result = IstecPutDevConfig (Config)) != ieDone) {
938 return Result;
939 }
940
941 // Make the changes permanent
942 if ((Result = IstecMakePermanent ()) != ieDone) {
943 return Result;
944 }
945
946 // Ok, all done
947 return ieDone;
948 }
949
950
951
IstecGetShortNumbers(ShortNumberColl & ShortNumbers)952 int IstecGetShortNumbers (ShortNumberColl& ShortNumbers)
953 // Read the short numbers from the istec. The function may not be called if
954 // the firmware version is < 2.00!
955 {
956 PRECONDITION (FirmwareVersion >= 2.00);
957
958 unsigned char Buf [5] = {
959 CTI_START, CTI_QUERY, CTI_LOAD_NUMBER, 0, CTI_STOP
960 };
961
962 for (int I = 0; I < ShortNumbers.GetCount (); I++) {
963
964 // Get a pointer to the info
965 ShortNumberInfo* Info = ShortNumbers [I];
966
967 // Insert the memory address into the message
968 Buf [3] = Info->GetMemory ();
969
970 // Send the message
971 IstecWrite (Buf, sizeof (Buf));
972
973 // Get the reply
974 IstecMsg* Reply = IstecRead ();
975 int Result = Reply->GetError ();
976
977 if (Result == ieDone) {
978 // Check the reply
979 if (!Reply->IsCTIMsg ()) {
980 Result = ieInvalidReply;
981 } else if (Reply->At (1) != CTI_ACK ||
982 Reply->At (2) != CTI_LOAD_NUMBER ||
983 Reply->At (3) != Info->GetMemory ()) {
984 Result = ieCTIError;
985 } else {
986 // We got a valid reply, put it into the info struct
987 Info->Unpack (*Reply);
988 }
989 }
990
991 // Delete the message
992 delete Reply;
993
994 // Bail out if we had an error
995 if (Result != ieDone) {
996 return Result;
997 }
998
999 }
1000
1001 // Success
1002 return ieDone;
1003 }
1004
1005
1006
IstecPutShortNumbers(const ShortNumberColl & ShortNumbers)1007 int IstecPutShortNumbers (const ShortNumberColl& ShortNumbers)
1008 // Store the short numbers into the istec. The function may not be called if
1009 // the firmware version is < 2.00!
1010 {
1011 PRECONDITION (FirmwareVersion >= 2.00);
1012
1013 for (int I = 0; I < ShortNumbers.GetCount (); I++) {
1014
1015 // Get a pointer to the info
1016 const ShortNumberInfo* Info = ShortNumbers [I];
1017
1018 // Create a message from the info
1019 IstecMsg Msg (0);
1020 Info->Pack (Msg);
1021
1022 // Send the message
1023 IstecWrite (Msg.Data, Msg.Size);
1024
1025 // Get the reply
1026 IstecMsg* Reply = IstecRead ();
1027 int Result = Reply->GetError ();
1028
1029 if (Result == ieDone) {
1030 // Check the reply
1031 if (!Reply->IsCTIMsg ()) {
1032 Result = ieInvalidReply;
1033 } else if (Reply->At (1) != CTI_ACK ||
1034 Reply->At (2) != CTI_STORE_NUMBER ||
1035 Reply->At (3) != CTI_STOP) {
1036 Result = ieCTIError;
1037 }
1038 }
1039
1040 // Delete the message
1041 delete Reply;
1042
1043 // Bail out if we had an error
1044 if (Result != ieDone) {
1045 return Result;
1046 }
1047
1048 }
1049
1050 // Success
1051 return ieDone;
1052 }
1053
1054
1055
IstecGetDayNight(unsigned & DayNight)1056 int IstecGetDayNight (unsigned& DayNight)
1057 // Read the day/night setting from the istec. If the current firmware does
1058 // not support this, return CTI_VAL_DAY, else return the setting
1059 {
1060 static const unsigned char Msg [4] = {
1061 CTI_START, CTI_QUERY, CTI_DAY_NIGHT, CTI_STOP
1062 };
1063
1064 // Set the DayNight flag to day, in case an error occurs
1065 DayNight = CTI_VAL_DAY;
1066
1067 // Check for firmware < 2.0
1068 if (FirmwareVersion < 2.0) {
1069 // Return day
1070 return ieDone;
1071 }
1072
1073 // Send the message
1074 IstecWrite (Msg, sizeof (Msg));
1075
1076 // Get the reply
1077 IstecMsg* Reply = IstecRead ();
1078 int Result = Reply->GetError ();
1079
1080 if (Result == ieDone) {
1081 // Check the reply
1082 if (!Reply->IsCTIMsg ()) {
1083 Result = ieInvalidReply;
1084 } else if (Reply->Size != 5 ||
1085 Reply->At (1) != CTI_ACK ||
1086 Reply->At (2) != CTI_DAY_NIGHT ||
1087 Reply->At (4) != CTI_STOP) {
1088 Result = ieCTIError;
1089 } else {
1090 // This is a valid message, retrieve the day/night setting
1091 DayNight = Reply->At (3)? 1 : 0;
1092 }
1093 }
1094
1095 // Delete the message
1096 delete Reply;
1097
1098 // Return the result of the operation
1099 return Result;
1100 }
1101
1102
1103
IstecPutDayNight(unsigned DayNight)1104 int IstecPutDayNight (unsigned DayNight)
1105 // Set the active configuration. If the current firmware does not support
1106 // this, ignore the command (return ieDone).
1107 {
1108 unsigned char Msg [5] = {
1109 CTI_START, CTI_CONF, CTI_DAY_NIGHT, 0, CTI_STOP
1110 };
1111
1112 // Check for firmware < 2.0
1113 if (FirmwareVersion < 2.0) {
1114 // Nothing to do
1115 return ieDone;
1116 }
1117
1118 // Put the value into the message
1119 Msg [3] = DayNight? CTI_VAL_NIGHT : CTI_VAL_DAY;
1120
1121 // Send the message
1122 IstecWrite (Msg, sizeof (Msg));
1123
1124 // Get the reply
1125 IstecMsg* Reply = IstecRead ();
1126 int Result = Reply->GetError ();
1127
1128 if (Result == ieDone) {
1129 // Check the reply
1130 if (!Reply->IsCTIMsg ()) {
1131 Result = ieInvalidReply;
1132 } else if (Reply->Size != 5 ||
1133 Reply->At (1) != CTI_ACK ||
1134 Reply->At (2) != CTI_DAY_NIGHT ||
1135 Reply->At (4) != CTI_STOP) {
1136 Result = ieCTIError;
1137 } else {
1138 Result = MapCTIError (Reply->At (3));
1139 }
1140 }
1141
1142 // Delete the message
1143 delete Reply;
1144
1145 // Return the result of the operation
1146 return Result;
1147 }
1148
1149
1150
IstecPutAlarm(unsigned Alarm)1151 int IstecPutAlarm (unsigned Alarm)
1152 // Set the alarm byte. If the current firmware does not support this, ignore
1153 // the command (return ieDone).
1154 {
1155 unsigned char Msg [5] = {
1156 CTI_START, CTI_CONF, CTI_ALARM, CTI_VAL_ALARM_OFF, CTI_STOP
1157 };
1158
1159 // Check for firmware < 2.0
1160 if (FirmwareVersion < 2.0) {
1161 // Nothing to do
1162 return ieDone;
1163 }
1164
1165 // Put the value into the message
1166 Msg [3] = (unsigned char) Alarm;
1167
1168 // Send the message
1169 IstecWrite (Msg, sizeof (Msg));
1170
1171 // Get the reply
1172 IstecMsg* Reply = IstecRead ();
1173 int Result = Reply->GetError ();
1174
1175 if (Result == ieDone) {
1176 // Check the reply
1177 if (!Reply->IsCTIMsg ()) {
1178 Result = ieInvalidReply;
1179 } else if (Reply->Size != 4 ||
1180 Reply->At (1) != CTI_ACK ||
1181 Reply->At (2) != CTI_ALARM ||
1182 Reply->At (3) != CTI_STOP) {
1183 Result = ieCTIError;
1184 }
1185 }
1186
1187 // Delete the message
1188 delete Reply;
1189
1190 // Return the result of the operation
1191 return Result;
1192 }
1193
1194
1195
IstecDiagOn()1196 static void IstecDiagOn ()
1197 // Switch the istec into diag mode
1198 {
1199 static unsigned char Msg [6] = { 0xdd, 0x00, 0x69, 0x5a, 0x96, 0xa5 };
1200 IstecWrite (Msg, sizeof (Msg));
1201 }
1202
1203
1204
IstecDiagOff()1205 static void IstecDiagOff ()
1206 // Disable diag mode
1207 {
1208 static unsigned char Msg [6] = { 0xdd, 0x01, 0x00, 0x00, 0x00, 0x00 };
1209 IstecWrite (Msg, sizeof (Msg));
1210 }
1211
1212
1213
EnableDiagMode()1214 void EnableDiagMode ()
1215 // This will decrement the diag mode counter. If it is zero, the command for
1216 // diag mode enable will be sent to the istec.
1217 {
1218 // If the counter is zero, we have a problem...
1219 CHECK (DiagModeCounter > 0);
1220
1221 // Decrement the counter, enable diag mode
1222 if (--DiagModeCounter == 0) {
1223 IstecDiagOn ();
1224 }
1225 }
1226
1227
1228
UpdateDiagMode()1229 void UpdateDiagMode ()
1230 // Send a "diag mode on" command to the istec if diag mode is currently
1231 // enabled.
1232 {
1233 if (DiagModeCounter == 0) {
1234 IstecDiagOn ();
1235 }
1236 }
1237
1238
1239
DisableDiagMode()1240 void DisableDiagMode ()
1241 // Increment the diag mode counter. If there is a transition from 0 to 1,
1242 // send the "diag mode off" command to the istec.
1243 {
1244 // The counter may not be less than zero
1245 PRECONDITION (DiagModeCounter >= 0);
1246
1247 // Increment the counter, disable diag mode
1248 if (++DiagModeCounter == 1) {
1249 IstecDiagOff ();
1250 }
1251 }
1252
1253
1254
IstecRingOn(unsigned char Device)1255 void IstecRingOn (unsigned char Device)
1256 // Ring a phone. Device is 0..n
1257 {
1258 static unsigned char Msg [6] = { 0xdd, 0x03, 0x00, 0x00, 0x01, 0x00 };
1259 Msg [2] = Device;
1260 IstecWrite (Msg, sizeof (Msg));
1261 }
1262
1263
1264
IstecRingOff(unsigned char Device)1265 void IstecRingOff (unsigned char Device)
1266 // Bell off. Device is 0..n
1267 {
1268 static unsigned char Msg [6] = { 0xdd, 0x03, 0x00, 0x00, 0x00, 0x00 };
1269 Msg [2] = Device;
1270 IstecWrite (Msg, sizeof (Msg));
1271 }
1272
1273
1274
1275