uart.c

Go to the documentation of this file.
00001 /*-
00002  * Public platform independent Near Field Communication (NFC) library
00003  * 
00004  * Copyright (C) 2009, 2010, Roel Verdult, Romuald Conty
00005  * 
00006  * This program is free software: you can redistribute it and/or modify it
00007  * under the terms of the GNU Lesser General Public License as published by the
00008  * Free Software Foundation, either version 3 of the License, or (at your
00009  * option) any later version.
00010  * 
00011  * This program is distributed in the hope that it will be useful, but WITHOUT
00012  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00014  * more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  *
00019  */
00020 
00028 /*
00029 Based on RS232 code written by Teunis van Beelen available:
00030 http://www.teuniz.net/RS-232/index.html
00031 */
00032 
00033 #ifdef HAVE_CONFIG_H
00034 #  include "config.h"
00035 #endif // HAVE_CONFIG_H
00036 
00037 #include "uart.h"
00038 
00039 #include <nfc/nfc.h>
00040 #include <nfc/nfc-messages.h>
00041 
00042 // Test if we are dealing with unix operating systems
00043 #ifndef _WIN32
00044 
00045 #  include <sys/select.h>
00046 #  include <termios.h>
00047 typedef struct termios term_info;
00048 typedef struct {
00049   int     fd;                   // Serial port file descriptor
00050   term_info tiOld;              // Terminal info before using the port
00051   term_info tiNew;              // Terminal info during the transaction
00052 } serial_port_unix;
00053 
00054 // timeval struct that define timeout delay for serial port
00055 const struct timeval timeout = {
00056   .tv_sec = 0,                  // 0 second
00057   .tv_usec = 60000              // 60 ms
00058 };
00059 
00060 // Work-around to claim uart interface using the c_iflag (software input processing) from the termios struct
00061 #  define CCLAIMED 0x80000000
00062 
00063 serial_port
00064 uart_open (const char *pcPortName)
00065 {
00066   serial_port_unix *sp = malloc (sizeof (serial_port_unix));
00067 
00068   if (sp == 0)
00069     return INVALID_SERIAL_PORT;
00070 
00071   sp->fd = open (pcPortName, O_RDWR | O_NOCTTY | O_NONBLOCK);
00072   if (sp->fd == -1) {
00073     uart_close (sp);
00074     return INVALID_SERIAL_PORT;
00075   }
00076 
00077   if (tcgetattr (sp->fd, &sp->tiOld) == -1) {
00078     uart_close (sp);
00079     return INVALID_SERIAL_PORT;
00080   }
00081   // Make sure the port is not claimed already
00082   if (sp->tiOld.c_iflag & CCLAIMED) {
00083     uart_close (sp);
00084     return CLAIMED_SERIAL_PORT;
00085   }
00086   // Copy the old terminal info struct
00087   sp->tiNew = sp->tiOld;
00088 
00089   sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD;
00090   sp->tiNew.c_iflag = CCLAIMED | IGNPAR;
00091   sp->tiNew.c_oflag = 0;
00092   sp->tiNew.c_lflag = 0;
00093 
00094   sp->tiNew.c_cc[VMIN] = 0;     // block until n bytes are received
00095   sp->tiNew.c_cc[VTIME] = 0;    // block until a timer expires (n * 100 mSec.)
00096 
00097   if (tcsetattr (sp->fd, TCSANOW, &sp->tiNew) == -1) {
00098     uart_close (sp);
00099     return INVALID_SERIAL_PORT;
00100   }
00101 
00102   tcflush (sp->fd, TCIFLUSH);
00103   return sp;
00104 }
00105 
00106 void
00107 uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
00108 {
00109   DBG ("Serial port speed requested to be set to %d bauds.", uiPortSpeed);
00110   const serial_port_unix *spu = (serial_port_unix *) sp;
00111 
00112   // Portability note: on some systems, B9600 != 9600 so we have to do
00113   // uint32_t <=> speed_t associations by hand.
00114   speed_t stPortSpeed = B9600;
00115   switch (uiPortSpeed) {
00116   case 9600:
00117     stPortSpeed = B9600;
00118     break;
00119   case 19200:
00120     stPortSpeed = B19200;
00121     break;
00122   case 38400:
00123     stPortSpeed = B38400;
00124     break;
00125 #  ifdef B57600
00126   case 57600:
00127     stPortSpeed = B57600;
00128     break;
00129 #  endif
00130 #  ifdef B115200
00131   case 115200:
00132     stPortSpeed = B115200;
00133     break;
00134 #  endif
00135 #  ifdef B230400
00136   case 230400:
00137     stPortSpeed = B230400;
00138     break;
00139 #  endif
00140 #  ifdef B460800
00141   case 460800:
00142     stPortSpeed = B460800;
00143     break;
00144 #  endif
00145   default:
00146     ERR ("Unable to set serial port speed to %d bauds. Speed value must be one of those defined in termios(3).",
00147          uiPortSpeed);
00148   };
00149 
00150   // Set port speed (Input and Output)
00151   cfsetispeed ((struct termios *) &(spu->tiNew), stPortSpeed);
00152   cfsetospeed ((struct termios *) &(spu->tiNew), stPortSpeed);
00153   if (tcsetattr (spu->fd, TCSADRAIN, &(spu->tiNew)) == -1) {
00154     ERR ("%s", "Unable to apply new speed settings.");
00155   }
00156 }
00157 
00158 uint32_t
00159 uart_get_speed (serial_port sp)
00160 {
00161   uint32_t uiPortSpeed = 0;
00162   const serial_port_unix *spu = (serial_port_unix *) sp;
00163   switch (cfgetispeed (&spu->tiNew)) {
00164   case B9600:
00165     uiPortSpeed = 9600;
00166     break;
00167   case B19200:
00168     uiPortSpeed = 19200;
00169     break;
00170   case B38400:
00171     uiPortSpeed = 38400;
00172     break;
00173 #  ifdef B57600
00174   case B57600:
00175     uiPortSpeed = 57600;
00176     break;
00177 #  endif
00178 #  ifdef B115200
00179   case B115200:
00180     uiPortSpeed = 115200;
00181     break;
00182 #  endif
00183 #  ifdef B230400
00184   case B230400:
00185     uiPortSpeed = 230400;
00186     break;
00187 #  endif
00188 #  ifdef B460800
00189   case B460800:
00190     uiPortSpeed = 460800;
00191     break;
00192 #  endif
00193   }
00194 
00195   return uiPortSpeed;
00196 }
00197 
00198 void
00199 uart_close (const serial_port sp)
00200 {
00201   if (((serial_port_unix *) sp)->fd >= 0) {
00202     tcsetattr (((serial_port_unix *) sp)->fd, TCSANOW, &((serial_port_unix *) sp)->tiOld);
00203     close (((serial_port_unix *) sp)->fd);
00204   }
00205   free (sp);
00206 }
00207 
00213 int
00214 uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRxLen)
00215 {
00216   int     res;
00217   int     byteCount;
00218   fd_set  rfds;
00219   struct timeval tv;
00220 
00221   // Reset the output count  
00222   *pszRxLen = 0;
00223 
00224   do {
00225     // Reset file descriptor
00226     FD_ZERO (&rfds);
00227     FD_SET (((serial_port_unix *) sp)->fd, &rfds);
00228     tv = timeout;
00229     res = select (((serial_port_unix *) sp)->fd + 1, &rfds, NULL, NULL, &tv);
00230 
00231     // Read error
00232     if (res < 0) {
00233       DBG ("%s", "RX error.");
00234       return DEIO;
00235     }
00236     // Read time-out
00237     if (res == 0) {
00238       if (*pszRxLen == 0) {
00239         // Error, we received no data
00240         DBG ("%s", "RX time-out, buffer empty.");
00241         return DETIMEOUT;
00242       } else {
00243         // We received some data, but nothing more is available
00244         return 0;
00245       }
00246     }
00247     // Retrieve the count of the incoming bytes
00248     res = ioctl (((serial_port_unix *) sp)->fd, FIONREAD, &byteCount);
00249     if (res < 0) {
00250       return DEIO;
00251     }
00252     // There is something available, read the data
00253     res = read (((serial_port_unix *) sp)->fd, pbtRx + (*pszRxLen), byteCount);
00254 
00255     // Stop if the OS has some troubles reading the data
00256     if (res <= 0) {
00257       return DEIO;
00258     }
00259 
00260     *pszRxLen += res;
00261 
00262   } while (byteCount);
00263 
00264   return 0;
00265 }
00266 
00272 int
00273 uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTxLen)
00274 {
00275   int32_t res;
00276   size_t  szPos = 0;
00277   fd_set  rfds;
00278   struct timeval tv;
00279 
00280   while (szPos < szTxLen) {
00281     // Reset file descriptor
00282     FD_ZERO (&rfds);
00283     FD_SET (((serial_port_unix *) sp)->fd, &rfds);
00284     tv = timeout;
00285     res = select (((serial_port_unix *) sp)->fd + 1, NULL, &rfds, NULL, &tv);
00286 
00287     // Write error
00288     if (res < 0) {
00289       DBG ("%s", "TX error.");
00290       return DEIO;
00291     }
00292     // Write time-out
00293     if (res == 0) {
00294       DBG ("%s", "TX time-out.");
00295       return DETIMEOUT;
00296     }
00297     // Send away the bytes
00298     res = write (((serial_port_unix *) sp)->fd, pbtTx + szPos, szTxLen - szPos);
00299 
00300     // Stop if the OS has some troubles sending the data
00301     if (res <= 0) {
00302       return DEIO;
00303     }
00304 
00305     szPos += res;
00306   }
00307   return 0;
00308 }
00309 
00310 #else
00311 // The windows serial port implementation
00312 
00313 typedef struct {
00314   HANDLE  hPort;                // Serial port handle
00315   DCB     dcb;                  // Device control settings
00316   COMMTIMEOUTS ct;              // Serial port time-out configuration
00317 } serial_port_windows;
00318 
00319 serial_port
00320 uart_open (const char *pcPortName)
00321 {
00322   char    acPortName[255];
00323   serial_port_windows *sp = malloc (sizeof (serial_port_windows));
00324 
00325   // Copy the input "com?" to "\\.\COM?" format
00326   sprintf (acPortName, "\\\\.\\%s", pcPortName);
00327   _strupr (acPortName);
00328 
00329   // Try to open the serial port
00330   sp->hPort = CreateFileA (acPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
00331   if (sp->hPort == INVALID_HANDLE_VALUE) {
00332     uart_close (sp);
00333     return INVALID_SERIAL_PORT;
00334   }
00335   // Prepare the device control
00336   memset (&sp->dcb, 0, sizeof (DCB));
00337   sp->dcb.DCBlength = sizeof (DCB);
00338   if (!BuildCommDCBA ("baud=9600 data=8 parity=N stop=1", &sp->dcb)) {
00339     uart_close (sp);
00340     return INVALID_SERIAL_PORT;
00341   }
00342   // Update the active serial port
00343   if (!SetCommState (sp->hPort, &sp->dcb)) {
00344     uart_close (sp);
00345     return INVALID_SERIAL_PORT;
00346   }
00347 
00348   sp->ct.ReadIntervalTimeout = 0;
00349   sp->ct.ReadTotalTimeoutMultiplier = 0;
00350   sp->ct.ReadTotalTimeoutConstant = 30;
00351   sp->ct.WriteTotalTimeoutMultiplier = 0;
00352   sp->ct.WriteTotalTimeoutConstant = 30;
00353 
00354   if (!SetCommTimeouts (sp->hPort, &sp->ct)) {
00355     uart_close (sp);
00356     return INVALID_SERIAL_PORT;
00357   }
00358 
00359   PurgeComm (sp->hPort, PURGE_RXABORT | PURGE_RXCLEAR);
00360 
00361   return sp;
00362 }
00363 
00364 void
00365 uart_close (const serial_port sp)
00366 {
00367   if (((serial_port_windows *) sp)->hPort != INVALID_HANDLE_VALUE) {
00368     CloseHandle (((serial_port_windows *) sp)->hPort);
00369   }
00370   free (sp);
00371 }
00372 
00373 void
00374 uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
00375 {
00376   serial_port_windows *spw;
00377 
00378   DBG ("Serial port speed requested to be set to %d bauds.", uiPortSpeed);
00379   // Set port speed (Input and Output)
00380   switch (uiPortSpeed) {
00381   case 9600:
00382   case 19200:
00383   case 38400:
00384   case 57600:
00385   case 115200:
00386   case 230400:
00387   case 460800:
00388     break;
00389   default:
00390     ERR
00391       ("Unable to set serial port speed to %d bauds. Speed value must be one of these constants: 9600 (default), 19200, 38400, 57600, 115200, 230400 or 460800.",
00392        uiPortSpeed);
00393   };
00394 
00395   spw = (serial_port_windows *) sp;
00396   spw->dcb.BaudRate = uiPortSpeed;
00397   if (!SetCommState (spw->hPort, &spw->dcb)) {
00398     ERR ("Unable to apply new speed settings.");
00399   }
00400 }
00401 
00402 uint32_t
00403 uart_get_speed (const serial_port sp)
00404 {
00405   const serial_port_windows *spw = (serial_port_windows *) sp;
00406   if (!GetCommState (spw->hPort, (serial_port) & spw->dcb))
00407     return spw->dcb.BaudRate;
00408 
00409   return 0;
00410 }
00411 
00412 int
00413 uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRxLen)
00414 {
00415   if (!ReadFile (((serial_port_windows *) sp)->hPort, pbtRx, *pszRxLen, (LPDWORD) pszRxLen, NULL)) {
00416     return DEIO;
00417   }
00418   if (!*pszRxLen)
00419     return DEIO;
00420   return 0;
00421 }
00422 
00423 int
00424 uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTxLen)
00425 {
00426   DWORD   dwTxLen = 0;
00427   if (!WriteFile (((serial_port_windows *) sp)->hPort, pbtTx, szTxLen, &dwTxLen, NULL)) {
00428     return DEIO;
00429   }
00430   if (!dwTxLen)
00431     return DEIO;
00432   return 0;
00433 }
00434 
00435 #endif /* _WIN32 */