Remove whitespace, update email addresses and URLs.
[kodak.git] / serial.cpp
1 //+----------------------------------------------------------------------------
2 //
3 // File:     serial.cpp
4 //
5 // Module:
6 //
7 // Synopsis:
8 //
9 // Copyright (C) 1999-2000 Scott Gasch <[email protected]>
10 //
11 // Created:  12 Jan 2000
12 //
13 //+----------------------------------------------------------------------------
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <sys/types.h>
19 #include <sys/uio.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <termios.h>
23
24 #include "global.h"
25 #include "debug.h"
26 #include "utils.h"
27 #include "serial.h"
28 #include "trace.h"
29
30 //+----------------------------------------------------------------------------
31 //
32 // Function:  CSerialPort
33 //
34 // Synopsis:
35 //
36 // Arguments: void
37 //
38 // Returns:   void
39 //
40 // History:   sgasch Created Header    12 Jan 2000
41 //
42 //+----------------------------------------------------------------------------
43 CSerialPort::CSerialPort(void)
44 {
45         CSerialPort("/dev/cuaa0");
46 }
47
48
49 //+----------------------------------------------------------------------------
50 //
51 // Function:  CSerialPort
52 //
53 // Synopsis:
54 //
55 // Arguments: char *szDevice
56 //
57 // Returns:   void
58 //
59 // History:   sgasch Created Header    12 Jan 2000
60 //
61 //+----------------------------------------------------------------------------
62 CSerialPort::CSerialPort(char *szDevice)
63 {
64         //
65         // Precondition: the path of the serial port device passed in is valid.
66         //
67         ASSERT(GOOD_PTR(szDevice));
68         if (!GOOD_PTR(szDevice))
69         {
70                 Trace("CSerialPort: Bad device argument, aborting.\n");
71                 return;
72         }
73
74         //
75         // Initialize identifiers
76         //
77         _iFd = 0;
78         _iBaudRate = 0;
79         _fInitialized = false;
80         _iTimeoutSec = 30;
81         
82         //
83         // Attempt to setup the serial line parameters.
84         //
85         if (false == SetupLine(szDevice))
86         {
87                 Trace("CSerialPort: Could not setup line.\n");
88                 return;
89         }
90
91         //
92         // Success
93         //
94         _fInitialized = true;
95         ASSERT(_iFd);
96 }
97
98
99 //+----------------------------------------------------------------------------
100 //
101 // Function:  CSerialPort
102 //
103 // Synopsis:
104 //
105 // Arguments: void
106 //
107 // Returns:   void
108 //
109 // History:   sgasch Created Header    12 Jan 2000
110 //
111 //+----------------------------------------------------------------------------
112 CSerialPort::~CSerialPort(void)
113 {
114         _fInitialized = false;
115
116         //
117         // Close the file descriptor if it is currently open.
118         //
119         if (_iFd)
120         {
121                 close(_iFd);
122         }
123 }
124
125 //+----------------------------------------------------------------------------
126 //
127 // Function:  SetTimeout
128 //
129 // Synopsis:
130 //
131 // Arguments: int iNewTimeout
132 //
133 // Returns:   void
134 //
135 // History:   sgasch Created Header    12 Jan 2000
136 //
137 //+----------------------------------------------------------------------------
138 void CSerialPort::SetTimeout(int iNewTimeout)
139 {
140         ASSERT(iNewTimeout >= 0);
141         _iTimeoutSec = iNewTimeout;
142 }
143
144 //+----------------------------------------------------------------------------
145 //
146 // Function:  GetTimeout
147 //
148 // Synopsis:
149 //
150 // Arguments: void
151 //
152 // Returns:   int
153 //
154 // History:   sgasch Created Header    12 Jan 2000
155 //
156 //+----------------------------------------------------------------------------
157 int CSerialPort::GetTimeout(void)
158 {
159         return(_iTimeoutSec);
160 }
161
162 //+----------------------------------------------------------------------------
163 //
164 // Function:  Read
165 //
166 // Synopsis:
167 //
168 // Arguments: BYTE *buf,
169 //            int iNumBytes
170 //
171 // Returns:   bool
172 //
173 // History:   sgasch Created Header    12 Jan 2000
174 //
175 //+----------------------------------------------------------------------------
176 bool CSerialPort::Read(BYTE *buf, int iNumBytes)
177 {
178         int iStatus = -1;                      // status returned from read call
179         int iBytesRead = 0;                    // number of bytes read so far
180         BYTE *bData = buf;                     // buffer to read into
181         int iRemainingBytes;                   // number of bytes remaining to read
182         int iTimeout = 0;
183
184         //
185         // Precondition: the byte pointer is valid and we are initialized.
186         //
187         if (!GOOD_PTR(buf))
188         {
189                 Trace("Read: Bad input argument, aborting.\n");
190                 ASSERT(false);
191                 return(false);
192         }
193
194         //
195         // Loop whie we have not yet read the full message.
196         //
197         do
198         {
199                 iRemainingBytes = iNumBytes - iBytesRead;
200                 iStatus = read(_iFd, bData, iRemainingBytes);
201
202                 if (iStatus <= 0)
203                 {
204                         //
205                         // The read failed.  This might be becase we are reading
206                         // too fast and there is no data on the port.  In this
207                         // case we will sleep for a second to allow the camera to
208                         // fill the input buffer before we read again.  If this is
209                         // not the case we have a read error.
210                         //
211                         if (EAGAIN != errno)
212                         {
213                                 Trace("Read: read error on the serial port.\n");
214                                 ASSERT(false);
215                                 return(false);
216                         }
217                         else
218                         {
219                                 if (iTimeout >= _iTimeoutSec)
220                                 {
221                                         Trace("Read: timeout waiting for data on serial port.\n");
222                                         return(false);
223                                 }
224                                 iTimeout++;
225                                 sleep(1);
226                         }
227                 }
228                 else
229                 {
230                         //
231                         // The read succeeded but it still might not have gotten the
232                         // full message.  We can find out by checking the return value
233                         // of the read call.
234                         //
235                         iBytesRead += iStatus;
236                         bData += iStatus;
237                         iStatus = -1;
238                 }
239         }
240         while(iBytesRead < iNumBytes);
241         ASSERT(iBytesRead == iNumBytes);
242
243         //
244         // Success
245         //
246         return(true);
247 }
248
249 //+----------------------------------------------------------------------------
250 //
251 // Function:  Write
252 //
253 // Synopsis:
254 //
255 // Arguments: BYTE *buf,
256 //            int iNumBytes
257 //
258 // Returns:   bool
259 //
260 // History:   sgasch Created Header    12 Jan 2000
261 //
262 //+----------------------------------------------------------------------------
263 bool CSerialPort::Write(BYTE *buf, int iNumBytes)
264 {
265         int iStatus = -1;                      // status returned from write call
266         int iBytesWritten = 0;                 // number of bytes written so far
267         BYTE *bData = buf;                     // data to write
268         int iRemainingBytes;                   // number of bytes remaining write
269         int iTimeout = 0;
270         
271         //
272         // Precondition: the byte pointer is valid and we are initialized.
273         //
274         if (!GOOD_PTR(buf))
275         {
276                 Trace("Write: Bad input argument, aborting.\n");
277                 ASSERT(false);
278                 return(false);
279         }
280
281         //
282         // Loop while we have not yet sent the full message to the port.
283         //
284         do
285         {
286                 iRemainingBytes = iNumBytes - iBytesWritten;
287                 iStatus = write(_iFd, bData, iRemainingBytes);
288                 if (iStatus <= 0)
289                 {
290                         //
291                         // An error occurred.  If we get EAGAIN in errno it means that
292                         // the port can't take our write right now, we'll sleep and try
293                         // again next loop.  Otherwise we have a write error.
294                         //
295                         if (EAGAIN != errno)
296                         {
297                                 Trace("Write: write error on the serial port.\n");
298                                 ASSERT(false);
299                                 return(false);
300                         }
301                         else
302                         {
303                                 if (iTimeout >= _iTimeoutSec)
304                                 {
305                                         Trace("Write: timeout on the serial port.");
306                                         return(false);
307                                 }
308                                 iTimeout++;
309                                 sleep(1);
310                         }
311                 }
312                 else
313                 {
314                         //
315                         // Write was successful but it still not have written the full
316                         // message.  We find out by seeing how many bytes it did write.
317                         //
318                         iBytesWritten += iStatus;
319                         bData += iStatus;
320                         iStatus = -1;
321                 }
322         }
323         while (iBytesWritten < iNumBytes);
324         ASSERT(iBytesWritten == iNumBytes);
325
326         //
327         // Success
328         //
329         return(true);
330 }
331
332 //+----------------------------------------------------------------------------
333 //
334 // Function:  SetBaudrate
335 //
336 // Synopsis:
337 //
338 // Arguments: int iBps
339 //
340 // Returns:   bool
341 //
342 // History:   sgasch Created Header    12 Jan 2000
343 //
344 //+----------------------------------------------------------------------------
345 bool CSerialPort::SetBaudrate(int iBps)
346 {
347         struct termios sTerm;                  // serial port configuration data
348
349         //
350         // Preconditions: the baud rate is supported by the camera.
351         //
352         if ((iBps != 9600) &&
353                 (iBps != 19200) &&
354                 (iBps != 38400) &&
355                 (iBps != 57600) &&
356                 (iBps != 115200))
357         {
358                 Trace("SetBaudRate: illegal rate.\n");
359                 return(false);
360         }
361
362         //
363         // Get the terios structure of the file descriptor we are using to
364         // talk to the com port... we want to set up in a way that the
365         // camera can understand.
366         //
367         if (tcgetattr(_iFd, &sTerm) < 0)
368         {
369                 Trace("SetBaudRate: could not get termios struct for serial fd."
370                           "\n");
371                 ASSERT(false);
372                 return(false);
373         }
374
375         //
376         // Set the input and output baud rates to the new setting.
377         //
378         if (cfsetispeed(&sTerm, iBps) < 0)
379         {
380                 Trace("SetBaudRate: could not set baud rate.\n");
381                 ASSERT(false);
382                 return(false);
383         }
384
385         if (cfsetospeed(&sTerm, iBps) < 0)
386         {
387                 Trace("SetBaudRate: could not set baud rate.\n");
388                 ASSERT(false);
389                 return(false);
390         }
391
392         //
393         // Make it "raw" (no cooked I/O)
394         //
395         cfmakeraw(&sTerm);
396
397         //
398         // 8 data bits
399         //
400         sTerm.c_cflag |= CS8;
401
402         //
403         // 1 stop bit
404         //
405         sTerm.c_cflag &= ~CSTOPB;
406
407         //
408         // Non-canonical mode settings
409         //
410         sTerm.c_cc[VTIME] = 5;
411         sTerm.c_cc[VMIN] = 255;
412
413         //
414         // Apply these termios settings.
415         //
416         if (tcsetattr(_iFd, 0, &sTerm) < 0)
417         {
418                 Trace("SetBaudRate: could not set termios struct for serial fd."
419                           "\n");
420                 ASSERT(false);
421                 return(false);
422         }
423
424         _iBaudRate = iBps;
425
426         //
427         // Success
428         //
429         return(true);
430 }
431
432 //+----------------------------------------------------------------------------
433 //
434 // Function:  GetBaudrate
435 //
436 // Synopsis:
437 //
438 // Arguments: void
439 //
440 // Returns:   int
441 //
442 // History:   sgasch Created Header    12 Jan 2000
443 //
444 //+----------------------------------------------------------------------------
445 int CSerialPort::GetBaudrate(void)
446 {
447         return(_iBaudRate);
448 }
449
450 //+----------------------------------------------------------------------------
451 //
452 // Function:  SetupLine (private)
453 //
454 // Synopsis:
455 //
456 // Arguments: char *szDevice
457 //
458 // Returns:   bool
459 //
460 // History:   sgasch Created Header    12 Jan 2000
461 //
462 //+----------------------------------------------------------------------------
463 bool CSerialPort::SetupLine(char *szDevice)
464 {
465         int iStatus = -1;                      // status from open call
466         struct termios sTerm;                  // line characteristics struct
467
468         //
469         // TODO: UUCP locking code here?
470         //
471
472         //
473         // Open the serial port in read/write mode.
474         //
475         iStatus = open(szDevice, O_RDWR);
476         if (iStatus < 0)
477         {
478            Trace("SetupSerialLine: Unable to open serial port.\n");
479            ASSERT(false);
480            return(false);
481         }
482         _iFd = iStatus;
483
484         //
485         // Use fcntl to tell the system we do not want to block on I/O from
486         // the file descriptor.  All read/write calls are asynchronous.
487         //
488         if (fcntl(iStatus, F_SETFL, O_NONBLOCK) < 0)
489         {
490                 Trace("SetupSerialLine: fcntl call failed, cannot use non-blocking "
491                           "I/O.\n");
492                 ASSERT(false);
493                 return(false);
494         }
495
496         //
497         // Get the terios structure of the file descriptor we are using to talk
498         // to the com port... we want to set up in a way that the camera can
499         // understand.
500         //
501         if (tcgetattr(iStatus, &sTerm) < 0)
502         {
503                 Trace("SetupSerialLine: could not get termios struct for serial "
504                           "fd.\n");
505                 ASSERT(false);
506                 return(false);
507         }
508
509         //
510         // Make it "raw" (no cooked I/O).
511         //
512         (void) cfmakeraw(&sTerm);
513
514         //
515         // The camera defaults to 9600 bps.
516         //
517         if (false == SetBaudrate(9600))
518         {
519                 Trace("SetupLine: could not set baud rate to 9600?\n");
520                 ASSERT(false);
521                 return(false);
522         }
523
524         //
525         // 8 data bits
526         //
527         sTerm.c_cflag |= CS8;
528
529         //
530         // 1 stop bit
531         //
532         sTerm.c_cflag &= ~CSTOPB;
533
534         //
535         // Don't echo back anything at all...
536         //
537         sTerm.c_lflag &= ~ECHO;
538         sTerm.c_lflag &= ~ECHOE;
539         sTerm.c_lflag &= ~ECHOCTL;
540         sTerm.c_lflag &= ~ECHOKE;
541         sTerm.c_lflag &= ~ECHONL;
542         sTerm.c_lflag &= ~ECHOPRT;
543
544         //
545         // There are no special control characters like KILL or ERASE...
546         //
547         sTerm.c_lflag &= ~IEXTEN;
548         sTerm.c_lflag &= ~ICANON;
549
550         //
551         // Don't send stop/start characters (flow control)
552         //
553         sTerm.c_iflag &= ~IXOFF;
554
555         //
556         // Non-canonical mode settings
557         //
558         sTerm.c_cc[VTIME] = 5;
559         sTerm.c_cc[VMIN] = 255;
560
561         //
562         // Apply these termios settings.
563         //
564         if (tcsetattr(iStatus, 0, &sTerm) < 0)
565         {
566                 Trace("SetupSerialLine: could not set termios struct for serial "
567                           "fd.\n");
568                 ASSERT(false);
569                 return(false);
570         }
571
572         //
573         // Success
574         //
575         return(true);
576 }
577
578
579
580 bool CSerialPort::WriteByte(BYTE bData)
581 {
582         BYTE x = bData;
583         
584         return(Write(&x, 1));
585 }
586
587