//+---------------------------------------------------------------------------- // // File: serial.cpp // // Module: // // Synopsis: // // Copyright (C) 1999-2000 Scott Gasch // // Created: 12 Jan 2000 // //+---------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include "global.h" #include "debug.h" #include "utils.h" #include "serial.h" #include "trace.h" //+---------------------------------------------------------------------------- // // Function: CSerialPort // // Synopsis: // // Arguments: void // // Returns: void // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- CSerialPort::CSerialPort(void) { CSerialPort("/dev/cuaa0"); } //+---------------------------------------------------------------------------- // // Function: CSerialPort // // Synopsis: // // Arguments: char *szDevice // // Returns: void // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- CSerialPort::CSerialPort(char *szDevice) { // // Precondition: the path of the serial port device passed in is valid. // ASSERT(GOOD_PTR(szDevice)); if (!GOOD_PTR(szDevice)) { Trace("CSerialPort: Bad device argument, aborting.\n"); return; } // // Initialize identifiers // _iFd = 0; _iBaudRate = 0; _fInitialized = false; _iTimeoutSec = 30; // // Attempt to setup the serial line parameters. // if (false == SetupLine(szDevice)) { Trace("CSerialPort: Could not setup line.\n"); return; } // // Success // _fInitialized = true; ASSERT(_iFd); } //+---------------------------------------------------------------------------- // // Function: CSerialPort // // Synopsis: // // Arguments: void // // Returns: void // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- CSerialPort::~CSerialPort(void) { _fInitialized = false; // // Close the file descriptor if it is currently open. // if (_iFd) { close(_iFd); } } //+---------------------------------------------------------------------------- // // Function: SetTimeout // // Synopsis: // // Arguments: int iNewTimeout // // Returns: void // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- void CSerialPort::SetTimeout(int iNewTimeout) { ASSERT(iNewTimeout >= 0); _iTimeoutSec = iNewTimeout; } //+---------------------------------------------------------------------------- // // Function: GetTimeout // // Synopsis: // // Arguments: void // // Returns: int // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- int CSerialPort::GetTimeout(void) { return(_iTimeoutSec); } //+---------------------------------------------------------------------------- // // Function: Read // // Synopsis: // // Arguments: BYTE *buf, // int iNumBytes // // Returns: bool // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- bool CSerialPort::Read(BYTE *buf, int iNumBytes) { int iStatus = -1; // status returned from read call int iBytesRead = 0; // number of bytes read so far BYTE *bData = buf; // buffer to read into int iRemainingBytes; // number of bytes remaining to read int iTimeout = 0; // // Precondition: the byte pointer is valid and we are initialized. // if (!GOOD_PTR(buf)) { Trace("Read: Bad input argument, aborting.\n"); ASSERT(false); return(false); } // // Loop whie we have not yet read the full message. // do { iRemainingBytes = iNumBytes - iBytesRead; iStatus = read(_iFd, bData, iRemainingBytes); if (iStatus <= 0) { // // The read failed. This might be becase we are reading // too fast and there is no data on the port. In this // case we will sleep for a second to allow the camera to // fill the input buffer before we read again. If this is // not the case we have a read error. // if (EAGAIN != errno) { Trace("Read: read error on the serial port.\n"); ASSERT(false); return(false); } else { if (iTimeout >= _iTimeoutSec) { Trace("Read: timeout waiting for data on serial port.\n"); return(false); } iTimeout++; sleep(1); } } else { // // The read succeeded but it still might not have gotten the // full message. We can find out by checking the return value // of the read call. // iBytesRead += iStatus; bData += iStatus; iStatus = -1; } } while(iBytesRead < iNumBytes); ASSERT(iBytesRead == iNumBytes); // // Success // return(true); } //+---------------------------------------------------------------------------- // // Function: Write // // Synopsis: // // Arguments: BYTE *buf, // int iNumBytes // // Returns: bool // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- bool CSerialPort::Write(BYTE *buf, int iNumBytes) { int iStatus = -1; // status returned from write call int iBytesWritten = 0; // number of bytes written so far BYTE *bData = buf; // data to write int iRemainingBytes; // number of bytes remaining write int iTimeout = 0; // // Precondition: the byte pointer is valid and we are initialized. // if (!GOOD_PTR(buf)) { Trace("Write: Bad input argument, aborting.\n"); ASSERT(false); return(false); } // // Loop while we have not yet sent the full message to the port. // do { iRemainingBytes = iNumBytes - iBytesWritten; iStatus = write(_iFd, bData, iRemainingBytes); if (iStatus <= 0) { // // An error occurred. If we get EAGAIN in errno it means that // the port can't take our write right now, we'll sleep and try // again next loop. Otherwise we have a write error. // if (EAGAIN != errno) { Trace("Write: write error on the serial port.\n"); ASSERT(false); return(false); } else { if (iTimeout >= _iTimeoutSec) { Trace("Write: timeout on the serial port."); return(false); } iTimeout++; sleep(1); } } else { // // Write was successful but it still not have written the full // message. We find out by seeing how many bytes it did write. // iBytesWritten += iStatus; bData += iStatus; iStatus = -1; } } while (iBytesWritten < iNumBytes); ASSERT(iBytesWritten == iNumBytes); // // Success // return(true); } //+---------------------------------------------------------------------------- // // Function: SetBaudrate // // Synopsis: // // Arguments: int iBps // // Returns: bool // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- bool CSerialPort::SetBaudrate(int iBps) { struct termios sTerm; // serial port configuration data // // Preconditions: the baud rate is supported by the camera. // if ((iBps != 9600) && (iBps != 19200) && (iBps != 38400) && (iBps != 57600) && (iBps != 115200)) { Trace("SetBaudRate: illegal rate.\n"); return(false); } // // Get the terios structure of the file descriptor we are using to // talk to the com port... we want to set up in a way that the // camera can understand. // if (tcgetattr(_iFd, &sTerm) < 0) { Trace("SetBaudRate: could not get termios struct for serial fd." "\n"); ASSERT(false); return(false); } // // Set the input and output baud rates to the new setting. // if (cfsetispeed(&sTerm, iBps) < 0) { Trace("SetBaudRate: could not set baud rate.\n"); ASSERT(false); return(false); } if (cfsetospeed(&sTerm, iBps) < 0) { Trace("SetBaudRate: could not set baud rate.\n"); ASSERT(false); return(false); } // // Make it "raw" (no cooked I/O) // cfmakeraw(&sTerm); // // 8 data bits // sTerm.c_cflag |= CS8; // // 1 stop bit // sTerm.c_cflag &= ~CSTOPB; // // Non-canonical mode settings // sTerm.c_cc[VTIME] = 5; sTerm.c_cc[VMIN] = 255; // // Apply these termios settings. // if (tcsetattr(_iFd, 0, &sTerm) < 0) { Trace("SetBaudRate: could not set termios struct for serial fd." "\n"); ASSERT(false); return(false); } _iBaudRate = iBps; // // Success // return(true); } //+---------------------------------------------------------------------------- // // Function: GetBaudrate // // Synopsis: // // Arguments: void // // Returns: int // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- int CSerialPort::GetBaudrate(void) { return(_iBaudRate); } //+---------------------------------------------------------------------------- // // Function: SetupLine (private) // // Synopsis: // // Arguments: char *szDevice // // Returns: bool // // History: sgasch Created Header 12 Jan 2000 // //+---------------------------------------------------------------------------- bool CSerialPort::SetupLine(char *szDevice) { int iStatus = -1; // status from open call struct termios sTerm; // line characteristics struct // // TODO: UUCP locking code here? // // // Open the serial port in read/write mode. // iStatus = open(szDevice, O_RDWR); if (iStatus < 0) { Trace("SetupSerialLine: Unable to open serial port.\n"); ASSERT(false); return(false); } _iFd = iStatus; // // Use fcntl to tell the system we do not want to block on I/O from // the file descriptor. All read/write calls are asynchronous. // if (fcntl(iStatus, F_SETFL, O_NONBLOCK) < 0) { Trace("SetupSerialLine: fcntl call failed, cannot use non-blocking " "I/O.\n"); ASSERT(false); return(false); } // // Get the terios structure of the file descriptor we are using to talk // to the com port... we want to set up in a way that the camera can // understand. // if (tcgetattr(iStatus, &sTerm) < 0) { Trace("SetupSerialLine: could not get termios struct for serial " "fd.\n"); ASSERT(false); return(false); } // // Make it "raw" (no cooked I/O). // (void) cfmakeraw(&sTerm); // // The camera defaults to 9600 bps. // if (false == SetBaudrate(9600)) { Trace("SetupLine: could not set baud rate to 9600?\n"); ASSERT(false); return(false); } // // 8 data bits // sTerm.c_cflag |= CS8; // // 1 stop bit // sTerm.c_cflag &= ~CSTOPB; // // Don't echo back anything at all... // sTerm.c_lflag &= ~ECHO; sTerm.c_lflag &= ~ECHOE; sTerm.c_lflag &= ~ECHOCTL; sTerm.c_lflag &= ~ECHOKE; sTerm.c_lflag &= ~ECHONL; sTerm.c_lflag &= ~ECHOPRT; // // There are no special control characters like KILL or ERASE... // sTerm.c_lflag &= ~IEXTEN; sTerm.c_lflag &= ~ICANON; // // Don't send stop/start characters (flow control) // sTerm.c_iflag &= ~IXOFF; // // Non-canonical mode settings // sTerm.c_cc[VTIME] = 5; sTerm.c_cc[VMIN] = 255; // // Apply these termios settings. // if (tcsetattr(iStatus, 0, &sTerm) < 0) { Trace("SetupSerialLine: could not set termios struct for serial " "fd.\n"); ASSERT(false); return(false); } // // Success // return(true); } bool CSerialPort::WriteByte(BYTE bData) { BYTE x = bData; return(Write(&x, 1)); }