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