Split UART implementations: POSIX and Windows
It could ease to port to other system like MiKey and it should be easy to track which is the concerned OS while uart driver is modified.
This commit is contained in:
parent
58dcf63e7c
commit
835823809f
4 changed files with 467 additions and 430 deletions
|
@ -20,14 +20,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file uart.c
|
* @file uart.c
|
||||||
* @brief UART driver
|
* @brief UART driver wrapper
|
||||||
*
|
|
||||||
* This file contains 2 implementations of UART driver: first for POSIX systems, second for Windows system.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Based on RS232 code written by Teunis van Beelen available:
|
|
||||||
http://www.teuniz.net/RS-232/index.html
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
|
@ -41,420 +34,9 @@ http://www.teuniz.net/RS-232/index.html
|
||||||
|
|
||||||
// Test if we are dealing with unix operating systems
|
// Test if we are dealing with unix operating systems
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
// The POSIX serial port implementation
|
||||||
# include <sys/select.h>
|
# include "uart_posix.c"
|
||||||
# include <termios.h>
|
|
||||||
typedef struct termios term_info;
|
|
||||||
typedef struct {
|
|
||||||
int fd; // Serial port file descriptor
|
|
||||||
term_info tiOld; // Terminal info before using the port
|
|
||||||
term_info tiNew; // Terminal info during the transaction
|
|
||||||
} serial_port_unix;
|
|
||||||
|
|
||||||
// timeval struct that define timeout delay for serial port
|
|
||||||
static struct timeval default_timeout = {
|
|
||||||
.tv_sec = 0, // 0 second
|
|
||||||
.tv_usec = 60000 // 60 ms
|
|
||||||
};
|
|
||||||
|
|
||||||
// Work-around to claim uart interface using the c_iflag (software input processing) from the termios struct
|
|
||||||
# define CCLAIMED 0x80000000
|
|
||||||
|
|
||||||
serial_port
|
|
||||||
uart_open (const char *pcPortName)
|
|
||||||
{
|
|
||||||
serial_port_unix *sp = malloc (sizeof (serial_port_unix));
|
|
||||||
|
|
||||||
if (sp == 0)
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
|
|
||||||
sp->fd = open (pcPortName, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
|
||||||
if (sp->fd == -1) {
|
|
||||||
uart_close (sp);
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tcgetattr (sp->fd, &sp->tiOld) == -1) {
|
|
||||||
uart_close (sp);
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
// Make sure the port is not claimed already
|
|
||||||
if (sp->tiOld.c_iflag & CCLAIMED) {
|
|
||||||
uart_close (sp);
|
|
||||||
return CLAIMED_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
// Copy the old terminal info struct
|
|
||||||
sp->tiNew = sp->tiOld;
|
|
||||||
|
|
||||||
sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD;
|
|
||||||
sp->tiNew.c_iflag = CCLAIMED | IGNPAR;
|
|
||||||
sp->tiNew.c_oflag = 0;
|
|
||||||
sp->tiNew.c_lflag = 0;
|
|
||||||
|
|
||||||
sp->tiNew.c_cc[VMIN] = 0; // block until n bytes are received
|
|
||||||
sp->tiNew.c_cc[VTIME] = 0; // block until a timer expires (n * 100 mSec.)
|
|
||||||
|
|
||||||
if (tcsetattr (sp->fd, TCSANOW, &sp->tiNew) == -1) {
|
|
||||||
uart_close (sp);
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
tcflush (sp->fd, TCIFLUSH);
|
|
||||||
return sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Remove PN53x related timeout
|
|
||||||
#define UART_TIMEOUT(X) ((X * 7) + 15000) // where X is 1-byte duration (µs): X * 6+1 bytes (PN53x's ACK + 1 other chance) + 15 ms (PN532 Tmax to reply ACK)
|
|
||||||
// UART_SPEED_T0_TIME(X) convert baud rate to interval between 2 bytes (in us)
|
|
||||||
#define UART_SPEED_T0_TIME(X) ((1000000 * 9)/ X) // 8,n,1 => 9 bits => data rate ~= bauds/9 (bytes/s); ex: 8N1@9600 ~= 1066 bytes/s
|
|
||||||
void
|
|
||||||
uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
|
|
||||||
{
|
|
||||||
long int iTimeout = UART_TIMEOUT(UART_SPEED_T0_TIME(uiPortSpeed));
|
|
||||||
DBG ("Serial port speed requested to be set to %d bauds (%ld µs).", uiPortSpeed, iTimeout);
|
|
||||||
const serial_port_unix *spu = (serial_port_unix *) sp;
|
|
||||||
|
|
||||||
// Portability note: on some systems, B9600 != 9600 so we have to do
|
|
||||||
// uint32_t <=> speed_t associations by hand.
|
|
||||||
speed_t stPortSpeed = B9600;
|
|
||||||
switch (uiPortSpeed) {
|
|
||||||
case 9600:
|
|
||||||
stPortSpeed = B9600;
|
|
||||||
break;
|
|
||||||
case 19200:
|
|
||||||
stPortSpeed = B19200;
|
|
||||||
break;
|
|
||||||
case 38400:
|
|
||||||
stPortSpeed = B38400;
|
|
||||||
break;
|
|
||||||
# ifdef B57600
|
|
||||||
case 57600:
|
|
||||||
stPortSpeed = B57600;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
# ifdef B115200
|
|
||||||
case 115200:
|
|
||||||
stPortSpeed = B115200;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
# ifdef B230400
|
|
||||||
case 230400:
|
|
||||||
stPortSpeed = B230400;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
# ifdef B460800
|
|
||||||
case 460800:
|
|
||||||
stPortSpeed = B460800;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
default:
|
|
||||||
ERR ("Unable to set serial port speed to %d bauds. Speed value must be one of those defined in termios(3).",
|
|
||||||
uiPortSpeed);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// Set default timeout
|
|
||||||
default_timeout.tv_usec = iTimeout;
|
|
||||||
|
|
||||||
// Set port speed (Input and Output)
|
|
||||||
cfsetispeed ((struct termios *) &(spu->tiNew), stPortSpeed);
|
|
||||||
cfsetospeed ((struct termios *) &(spu->tiNew), stPortSpeed);
|
|
||||||
if (tcsetattr (spu->fd, TCSADRAIN, &(spu->tiNew)) == -1) {
|
|
||||||
ERR ("%s", "Unable to apply new speed settings.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
uart_get_speed (serial_port sp)
|
|
||||||
{
|
|
||||||
uint32_t uiPortSpeed = 0;
|
|
||||||
const serial_port_unix *spu = (serial_port_unix *) sp;
|
|
||||||
switch (cfgetispeed (&spu->tiNew)) {
|
|
||||||
case B9600:
|
|
||||||
uiPortSpeed = 9600;
|
|
||||||
break;
|
|
||||||
case B19200:
|
|
||||||
uiPortSpeed = 19200;
|
|
||||||
break;
|
|
||||||
case B38400:
|
|
||||||
uiPortSpeed = 38400;
|
|
||||||
break;
|
|
||||||
# ifdef B57600
|
|
||||||
case B57600:
|
|
||||||
uiPortSpeed = 57600;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
# ifdef B115200
|
|
||||||
case B115200:
|
|
||||||
uiPortSpeed = 115200;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
# ifdef B230400
|
|
||||||
case B230400:
|
|
||||||
uiPortSpeed = 230400;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
# ifdef B460800
|
|
||||||
case B460800:
|
|
||||||
uiPortSpeed = 460800;
|
|
||||||
break;
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return uiPortSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
uart_close (const serial_port sp)
|
|
||||||
{
|
|
||||||
if (((serial_port_unix *) sp)->fd >= 0) {
|
|
||||||
tcsetattr (((serial_port_unix *) sp)->fd, TCSANOW, &((serial_port_unix *) sp)->tiOld);
|
|
||||||
close (((serial_port_unix *) sp)->fd);
|
|
||||||
}
|
|
||||||
free (sp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Receive data from UART and copy data to \a pbtRx
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise driver error code
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
int byteCount;
|
|
||||||
fd_set rfds;
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
// Reset the output count
|
|
||||||
*pszRx = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
// Reset file descriptor
|
|
||||||
FD_ZERO (&rfds);
|
|
||||||
FD_SET (((serial_port_unix *) sp)->fd, &rfds);
|
|
||||||
tv = default_timeout;
|
|
||||||
res = select (((serial_port_unix *) sp)->fd + 1, &rfds, NULL, NULL, &tv);
|
|
||||||
|
|
||||||
// Read error
|
|
||||||
if (res < 0) {
|
|
||||||
DBG ("%s", "RX error.");
|
|
||||||
return DEIO;
|
|
||||||
}
|
|
||||||
// Read time-out
|
|
||||||
if (res == 0) {
|
|
||||||
if (*pszRx == 0) {
|
|
||||||
// Error, we received no data
|
|
||||||
DBG ("RX time-out (%ld µs), buffer empty.", default_timeout.tv_usec);
|
|
||||||
return DETIMEOUT;
|
|
||||||
} else {
|
|
||||||
// We received some data, but nothing more is available
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve the count of the incoming bytes
|
|
||||||
res = ioctl (((serial_port_unix *) sp)->fd, FIONREAD, &byteCount);
|
|
||||||
if (res < 0) {
|
|
||||||
return DEIO;
|
|
||||||
}
|
|
||||||
// There is something available, read the data
|
|
||||||
res = read (((serial_port_unix *) sp)->fd, pbtRx + (*pszRx), byteCount);
|
|
||||||
|
|
||||||
// Stop if the OS has some troubles reading the data
|
|
||||||
if (res <= 0) {
|
|
||||||
return DEIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
*pszRx += res;
|
|
||||||
|
|
||||||
} while (byteCount);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Send \a pbtTx content to UART
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a driver error is returned
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx)
|
|
||||||
{
|
|
||||||
int32_t res;
|
|
||||||
size_t szPos = 0;
|
|
||||||
fd_set rfds;
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
while (szPos < szTx) {
|
|
||||||
// Reset file descriptor
|
|
||||||
FD_ZERO (&rfds);
|
|
||||||
FD_SET (((serial_port_unix *) sp)->fd, &rfds);
|
|
||||||
tv = default_timeout;
|
|
||||||
res = select (((serial_port_unix *) sp)->fd + 1, NULL, &rfds, NULL, &tv);
|
|
||||||
|
|
||||||
// Write error
|
|
||||||
if (res < 0) {
|
|
||||||
DBG ("%s", "TX error.");
|
|
||||||
return DEIO;
|
|
||||||
}
|
|
||||||
// Write time-out
|
|
||||||
if (res == 0) {
|
|
||||||
DBG ("%s", "TX time-out.");
|
|
||||||
return DETIMEOUT;
|
|
||||||
}
|
|
||||||
// Send away the bytes
|
|
||||||
res = write (((serial_port_unix *) sp)->fd, pbtTx + szPos, szTx - szPos);
|
|
||||||
|
|
||||||
// Stop if the OS has some troubles sending the data
|
|
||||||
if (res <= 0) {
|
|
||||||
return DEIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
szPos += res;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// The windows serial port implementation
|
// The windows serial port implementation
|
||||||
|
# include "uart_win32.c"
|
||||||
typedef struct {
|
|
||||||
HANDLE hPort; // Serial port handle
|
|
||||||
DCB dcb; // Device control settings
|
|
||||||
COMMTIMEOUTS ct; // Serial port time-out configuration
|
|
||||||
} serial_port_windows;
|
|
||||||
|
|
||||||
serial_port
|
|
||||||
uart_open (const char *pcPortName)
|
|
||||||
{
|
|
||||||
char acPortName[255];
|
|
||||||
serial_port_windows *sp = malloc (sizeof (serial_port_windows));
|
|
||||||
|
|
||||||
// Copy the input "com?" to "\\.\COM?" format
|
|
||||||
sprintf (acPortName, "\\\\.\\%s", pcPortName);
|
|
||||||
_strupr (acPortName);
|
|
||||||
|
|
||||||
// Try to open the serial port
|
|
||||||
sp->hPort = CreateFileA (acPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
||||||
if (sp->hPort == INVALID_HANDLE_VALUE) {
|
|
||||||
uart_close (sp);
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
// Prepare the device control
|
|
||||||
memset (&sp->dcb, 0, sizeof (DCB));
|
|
||||||
sp->dcb.DCBlength = sizeof (DCB);
|
|
||||||
if (!BuildCommDCBA ("baud=9600 data=8 parity=N stop=1", &sp->dcb)) {
|
|
||||||
uart_close (sp);
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
// Update the active serial port
|
|
||||||
if (!SetCommState (sp->hPort, &sp->dcb)) {
|
|
||||||
uart_close (sp);
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
sp->ct.ReadIntervalTimeout = 30;
|
|
||||||
sp->ct.ReadTotalTimeoutMultiplier = 0;
|
|
||||||
sp->ct.ReadTotalTimeoutConstant = 30;
|
|
||||||
sp->ct.WriteTotalTimeoutMultiplier = 30;
|
|
||||||
sp->ct.WriteTotalTimeoutConstant = 0;
|
|
||||||
|
|
||||||
if (!SetCommTimeouts (sp->hPort, &sp->ct)) {
|
|
||||||
uart_close (sp);
|
|
||||||
return INVALID_SERIAL_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
PurgeComm (sp->hPort, PURGE_RXABORT | PURGE_RXCLEAR);
|
|
||||||
|
|
||||||
return sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
uart_close (const serial_port sp)
|
|
||||||
{
|
|
||||||
if (((serial_port_windows *) sp)->hPort != INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle (((serial_port_windows *) sp)->hPort);
|
|
||||||
}
|
|
||||||
free (sp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Remove PN53x related timeout
|
|
||||||
void
|
|
||||||
uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
|
|
||||||
{
|
|
||||||
serial_port_windows *spw;
|
|
||||||
|
|
||||||
DBG ("Serial port speed requested to be set to %d bauds.", uiPortSpeed);
|
|
||||||
// Set port speed (Input and Output)
|
|
||||||
switch (uiPortSpeed) {
|
|
||||||
case 9600:
|
|
||||||
case 19200:
|
|
||||||
case 38400:
|
|
||||||
case 57600:
|
|
||||||
case 115200:
|
|
||||||
case 230400:
|
|
||||||
case 460800:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERR("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.", uiPortSpeed);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
spw = (serial_port_windows *) sp;
|
|
||||||
|
|
||||||
// Set timeouts
|
|
||||||
//printf ("UART_SPEED_T0_TIME (%d) = %d\n", uiPortSpeed, UART_SPEED_T0_TIME(uiPortSpeed));
|
|
||||||
int iTimeout = 200;
|
|
||||||
spw->ct.ReadIntervalTimeout = 2;
|
|
||||||
spw->ct.ReadTotalTimeoutMultiplier = 0;
|
|
||||||
spw->ct.ReadTotalTimeoutConstant = iTimeout;
|
|
||||||
spw->ct.WriteTotalTimeoutMultiplier = iTimeout;
|
|
||||||
spw->ct.WriteTotalTimeoutConstant = 0;
|
|
||||||
|
|
||||||
if (!SetCommTimeouts (spw->hPort, &spw->ct)) {
|
|
||||||
ERR ("Unable to apply new timeout settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set baud rate
|
|
||||||
spw->dcb.BaudRate = uiPortSpeed;
|
|
||||||
if (!SetCommState (spw->hPort, &spw->dcb)) {
|
|
||||||
ERR ("Unable to apply new speed settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PurgeComm (spw->hPort, PURGE_RXABORT | PURGE_RXCLEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
uart_get_speed (const serial_port sp)
|
|
||||||
{
|
|
||||||
const serial_port_windows *spw = (serial_port_windows *) sp;
|
|
||||||
if (!GetCommState (spw->hPort, (serial_port) & spw->dcb))
|
|
||||||
return spw->dcb.BaudRate;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
|
|
||||||
{
|
|
||||||
if (!ReadFile (((serial_port_windows *) sp)->hPort, pbtRx, (DWORD)(*pszRx), (LPDWORD) pszRx, NULL)) {
|
|
||||||
return DEIO;
|
|
||||||
}
|
|
||||||
if (!*pszRx)
|
|
||||||
return DEIO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx)
|
|
||||||
{
|
|
||||||
DWORD dwTxLen = 0;
|
|
||||||
if (!WriteFile (((serial_port_windows *) sp)->hPort, pbtTx, szTx, &dwTxLen, NULL)) {
|
|
||||||
return DEIO;
|
|
||||||
}
|
|
||||||
if (!dwTxLen)
|
|
||||||
return DEIO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
|
@ -43,22 +43,15 @@
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
# include <limits.h>
|
# include <limits.h>
|
||||||
# include <sys/time.h>
|
# include <sys/time.h>
|
||||||
|
|
||||||
// unistd.h is needed for usleep() fct.
|
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# define delay_ms( X ) usleep( X * 1000 )
|
# define delay_ms( X ) usleep( X * 1000 )
|
||||||
# else
|
# else
|
||||||
# include <windows.h>
|
# include "contrib/windows.h"
|
||||||
|
|
||||||
# define snprintf _snprintf
|
|
||||||
# define strdup _strdup
|
|
||||||
# define delay_ms( X ) Sleep( X )
|
# define delay_ms( X ) Sleep( X )
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
// Path to the serial port is OS-dependant.
|
// Path to the serial port is OS-dependant.
|
||||||
// Try to guess what we should use.
|
// Try to guess what we should use.
|
||||||
//
|
|
||||||
// XXX: Some review from users cross-compiling is welcome!
|
|
||||||
# if defined (_WIN32)
|
# if defined (_WIN32)
|
||||||
# define DEFAULT_SERIAL_PORTS { "COM1", "COM2", "COM3", "COM4", NULL }
|
# define DEFAULT_SERIAL_PORTS { "COM1", "COM2", "COM3", "COM4", NULL }
|
||||||
# elif defined(__APPLE__)
|
# elif defined(__APPLE__)
|
||||||
|
|
298
libnfc/buses/uart_posix.c
Normal file
298
libnfc/buses/uart_posix.c
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
/*-
|
||||||
|
* Public platform independent Near Field Communication (NFC) library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009, 2010, Roel Verdult, Romuald Conty
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file uart_posix.c
|
||||||
|
* @brief POSIX UART driver
|
||||||
|
*/
|
||||||
|
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <termios.h>
|
||||||
|
typedef struct termios term_info;
|
||||||
|
typedef struct {
|
||||||
|
int fd; // Serial port file descriptor
|
||||||
|
term_info tiOld; // Terminal info before using the port
|
||||||
|
term_info tiNew; // Terminal info during the transaction
|
||||||
|
} serial_port_unix;
|
||||||
|
|
||||||
|
// timeval struct that define timeout delay for serial port
|
||||||
|
static struct timeval default_timeout = {
|
||||||
|
.tv_sec = 0, // 0 second
|
||||||
|
.tv_usec = 60000 // 60 ms
|
||||||
|
};
|
||||||
|
|
||||||
|
// Work-around to claim uart interface using the c_iflag (software input processing) from the termios struct
|
||||||
|
# define CCLAIMED 0x80000000
|
||||||
|
|
||||||
|
serial_port
|
||||||
|
uart_open (const char *pcPortName)
|
||||||
|
{
|
||||||
|
serial_port_unix *sp = malloc (sizeof (serial_port_unix));
|
||||||
|
|
||||||
|
if (sp == 0)
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
|
||||||
|
sp->fd = open (pcPortName, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||||
|
if (sp->fd == -1) {
|
||||||
|
uart_close (sp);
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcgetattr (sp->fd, &sp->tiOld) == -1) {
|
||||||
|
uart_close (sp);
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
// Make sure the port is not claimed already
|
||||||
|
if (sp->tiOld.c_iflag & CCLAIMED) {
|
||||||
|
uart_close (sp);
|
||||||
|
return CLAIMED_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
// Copy the old terminal info struct
|
||||||
|
sp->tiNew = sp->tiOld;
|
||||||
|
|
||||||
|
sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD;
|
||||||
|
sp->tiNew.c_iflag = CCLAIMED | IGNPAR;
|
||||||
|
sp->tiNew.c_oflag = 0;
|
||||||
|
sp->tiNew.c_lflag = 0;
|
||||||
|
|
||||||
|
sp->tiNew.c_cc[VMIN] = 0; // block until n bytes are received
|
||||||
|
sp->tiNew.c_cc[VTIME] = 0; // block until a timer expires (n * 100 mSec.)
|
||||||
|
|
||||||
|
if (tcsetattr (sp->fd, TCSANOW, &sp->tiNew) == -1) {
|
||||||
|
uart_close (sp);
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcflush (sp->fd, TCIFLUSH);
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Remove PN53x related timeout
|
||||||
|
#define UART_TIMEOUT(X) ((X * 7) + 15000) // where X is 1-byte duration (µs): X * 6+1 bytes (PN53x's ACK + 1 other chance) + 15 ms (PN532 Tmax to reply ACK)
|
||||||
|
// UART_SPEED_T0_TIME(X) convert baud rate to interval between 2 bytes (in us)
|
||||||
|
#define UART_SPEED_T0_TIME(X) ((1000000 * 9)/ X) // 8,n,1 => 9 bits => data rate ~= bauds/9 (bytes/s); ex: 8N1@9600 ~= 1066 bytes/s
|
||||||
|
void
|
||||||
|
uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
|
||||||
|
{
|
||||||
|
long int iTimeout = UART_TIMEOUT(UART_SPEED_T0_TIME(uiPortSpeed));
|
||||||
|
DBG ("Serial port speed requested to be set to %d bauds (%ld µs).", uiPortSpeed, iTimeout);
|
||||||
|
const serial_port_unix *spu = (serial_port_unix *) sp;
|
||||||
|
|
||||||
|
// Portability note: on some systems, B9600 != 9600 so we have to do
|
||||||
|
// uint32_t <=> speed_t associations by hand.
|
||||||
|
speed_t stPortSpeed = B9600;
|
||||||
|
switch (uiPortSpeed) {
|
||||||
|
case 9600:
|
||||||
|
stPortSpeed = B9600;
|
||||||
|
break;
|
||||||
|
case 19200:
|
||||||
|
stPortSpeed = B19200;
|
||||||
|
break;
|
||||||
|
case 38400:
|
||||||
|
stPortSpeed = B38400;
|
||||||
|
break;
|
||||||
|
# ifdef B57600
|
||||||
|
case 57600:
|
||||||
|
stPortSpeed = B57600;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
# ifdef B115200
|
||||||
|
case 115200:
|
||||||
|
stPortSpeed = B115200;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
# ifdef B230400
|
||||||
|
case 230400:
|
||||||
|
stPortSpeed = B230400;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
# ifdef B460800
|
||||||
|
case 460800:
|
||||||
|
stPortSpeed = B460800;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
default:
|
||||||
|
ERR ("Unable to set serial port speed to %d bauds. Speed value must be one of those defined in termios(3).",
|
||||||
|
uiPortSpeed);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// Set default timeout
|
||||||
|
default_timeout.tv_usec = iTimeout;
|
||||||
|
|
||||||
|
// Set port speed (Input and Output)
|
||||||
|
cfsetispeed ((struct termios *) &(spu->tiNew), stPortSpeed);
|
||||||
|
cfsetospeed ((struct termios *) &(spu->tiNew), stPortSpeed);
|
||||||
|
if (tcsetattr (spu->fd, TCSADRAIN, &(spu->tiNew)) == -1) {
|
||||||
|
ERR ("%s", "Unable to apply new speed settings.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
uart_get_speed (serial_port sp)
|
||||||
|
{
|
||||||
|
uint32_t uiPortSpeed = 0;
|
||||||
|
const serial_port_unix *spu = (serial_port_unix *) sp;
|
||||||
|
switch (cfgetispeed (&spu->tiNew)) {
|
||||||
|
case B9600:
|
||||||
|
uiPortSpeed = 9600;
|
||||||
|
break;
|
||||||
|
case B19200:
|
||||||
|
uiPortSpeed = 19200;
|
||||||
|
break;
|
||||||
|
case B38400:
|
||||||
|
uiPortSpeed = 38400;
|
||||||
|
break;
|
||||||
|
# ifdef B57600
|
||||||
|
case B57600:
|
||||||
|
uiPortSpeed = 57600;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
# ifdef B115200
|
||||||
|
case B115200:
|
||||||
|
uiPortSpeed = 115200;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
# ifdef B230400
|
||||||
|
case B230400:
|
||||||
|
uiPortSpeed = 230400;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
# ifdef B460800
|
||||||
|
case B460800:
|
||||||
|
uiPortSpeed = 460800;
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return uiPortSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
uart_close (const serial_port sp)
|
||||||
|
{
|
||||||
|
if (((serial_port_unix *) sp)->fd >= 0) {
|
||||||
|
tcsetattr (((serial_port_unix *) sp)->fd, TCSANOW, &((serial_port_unix *) sp)->tiOld);
|
||||||
|
close (((serial_port_unix *) sp)->fd);
|
||||||
|
}
|
||||||
|
free (sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Receive data from UART and copy data to \a pbtRx
|
||||||
|
*
|
||||||
|
* @return 0 on success, otherwise driver error code
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int byteCount;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
// Reset the output count
|
||||||
|
*pszRx = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Reset file descriptor
|
||||||
|
FD_ZERO (&rfds);
|
||||||
|
FD_SET (((serial_port_unix *) sp)->fd, &rfds);
|
||||||
|
tv = default_timeout;
|
||||||
|
res = select (((serial_port_unix *) sp)->fd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
|
||||||
|
// Read error
|
||||||
|
if (res < 0) {
|
||||||
|
DBG ("%s", "RX error.");
|
||||||
|
return DEIO;
|
||||||
|
}
|
||||||
|
// Read time-out
|
||||||
|
if (res == 0) {
|
||||||
|
if (*pszRx == 0) {
|
||||||
|
// Error, we received no data
|
||||||
|
DBG ("RX time-out (%ld µs), buffer empty.", default_timeout.tv_usec);
|
||||||
|
return DETIMEOUT;
|
||||||
|
} else {
|
||||||
|
// We received some data, but nothing more is available
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Retrieve the count of the incoming bytes
|
||||||
|
res = ioctl (((serial_port_unix *) sp)->fd, FIONREAD, &byteCount);
|
||||||
|
if (res < 0) {
|
||||||
|
return DEIO;
|
||||||
|
}
|
||||||
|
// There is something available, read the data
|
||||||
|
res = read (((serial_port_unix *) sp)->fd, pbtRx + (*pszRx), byteCount);
|
||||||
|
|
||||||
|
// Stop if the OS has some troubles reading the data
|
||||||
|
if (res <= 0) {
|
||||||
|
return DEIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pszRx += res;
|
||||||
|
|
||||||
|
} while (byteCount);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send \a pbtTx content to UART
|
||||||
|
*
|
||||||
|
* @return 0 on success, otherwise a driver error is returned
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx)
|
||||||
|
{
|
||||||
|
int32_t res;
|
||||||
|
size_t szPos = 0;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
while (szPos < szTx) {
|
||||||
|
// Reset file descriptor
|
||||||
|
FD_ZERO (&rfds);
|
||||||
|
FD_SET (((serial_port_unix *) sp)->fd, &rfds);
|
||||||
|
tv = default_timeout;
|
||||||
|
res = select (((serial_port_unix *) sp)->fd + 1, NULL, &rfds, NULL, &tv);
|
||||||
|
|
||||||
|
// Write error
|
||||||
|
if (res < 0) {
|
||||||
|
DBG ("%s", "TX error.");
|
||||||
|
return DEIO;
|
||||||
|
}
|
||||||
|
// Write time-out
|
||||||
|
if (res == 0) {
|
||||||
|
DBG ("%s", "TX time-out.");
|
||||||
|
return DETIMEOUT;
|
||||||
|
}
|
||||||
|
// Send away the bytes
|
||||||
|
res = write (((serial_port_unix *) sp)->fd, pbtTx + szPos, szTx - szPos);
|
||||||
|
|
||||||
|
// Stop if the OS has some troubles sending the data
|
||||||
|
if (res <= 0) {
|
||||||
|
return DEIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
szPos += res;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
164
libnfc/buses/uart_win32.c
Normal file
164
libnfc/buses/uart_win32.c
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
/*-
|
||||||
|
* Public platform independent Near Field Communication (NFC) library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009, 2010, Roel Verdult, Romuald Conty
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file uart.c
|
||||||
|
* @brief Windows UART driver
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
HANDLE hPort; // Serial port handle
|
||||||
|
DCB dcb; // Device control settings
|
||||||
|
COMMTIMEOUTS ct; // Serial port time-out configuration
|
||||||
|
} serial_port_windows;
|
||||||
|
|
||||||
|
serial_port
|
||||||
|
uart_open (const char *pcPortName)
|
||||||
|
{
|
||||||
|
char acPortName[255];
|
||||||
|
serial_port_windows *sp = malloc (sizeof (serial_port_windows));
|
||||||
|
|
||||||
|
// Copy the input "com?" to "\\.\COM?" format
|
||||||
|
sprintf (acPortName, "\\\\.\\%s", pcPortName);
|
||||||
|
_strupr (acPortName);
|
||||||
|
|
||||||
|
// Try to open the serial port
|
||||||
|
sp->hPort = CreateFileA (acPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
if (sp->hPort == INVALID_HANDLE_VALUE) {
|
||||||
|
uart_close (sp);
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
// Prepare the device control
|
||||||
|
memset (&sp->dcb, 0, sizeof (DCB));
|
||||||
|
sp->dcb.DCBlength = sizeof (DCB);
|
||||||
|
if (!BuildCommDCBA ("baud=9600 data=8 parity=N stop=1", &sp->dcb)) {
|
||||||
|
uart_close (sp);
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
// Update the active serial port
|
||||||
|
if (!SetCommState (sp->hPort, &sp->dcb)) {
|
||||||
|
uart_close (sp);
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp->ct.ReadIntervalTimeout = 30;
|
||||||
|
sp->ct.ReadTotalTimeoutMultiplier = 0;
|
||||||
|
sp->ct.ReadTotalTimeoutConstant = 30;
|
||||||
|
sp->ct.WriteTotalTimeoutMultiplier = 30;
|
||||||
|
sp->ct.WriteTotalTimeoutConstant = 0;
|
||||||
|
|
||||||
|
if (!SetCommTimeouts (sp->hPort, &sp->ct)) {
|
||||||
|
uart_close (sp);
|
||||||
|
return INVALID_SERIAL_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
PurgeComm (sp->hPort, PURGE_RXABORT | PURGE_RXCLEAR);
|
||||||
|
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
uart_close (const serial_port sp)
|
||||||
|
{
|
||||||
|
if (((serial_port_windows *) sp)->hPort != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle (((serial_port_windows *) sp)->hPort);
|
||||||
|
}
|
||||||
|
free (sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Remove PN53x related timeout
|
||||||
|
void
|
||||||
|
uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
|
||||||
|
{
|
||||||
|
serial_port_windows *spw;
|
||||||
|
|
||||||
|
DBG ("Serial port speed requested to be set to %d bauds.", uiPortSpeed);
|
||||||
|
// Set port speed (Input and Output)
|
||||||
|
switch (uiPortSpeed) {
|
||||||
|
case 9600:
|
||||||
|
case 19200:
|
||||||
|
case 38400:
|
||||||
|
case 57600:
|
||||||
|
case 115200:
|
||||||
|
case 230400:
|
||||||
|
case 460800:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ERR("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.", uiPortSpeed);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
spw = (serial_port_windows *) sp;
|
||||||
|
|
||||||
|
// Set timeouts
|
||||||
|
//printf ("UART_SPEED_T0_TIME (%d) = %d\n", uiPortSpeed, UART_SPEED_T0_TIME(uiPortSpeed));
|
||||||
|
int iTimeout = 200;
|
||||||
|
spw->ct.ReadIntervalTimeout = 2;
|
||||||
|
spw->ct.ReadTotalTimeoutMultiplier = 0;
|
||||||
|
spw->ct.ReadTotalTimeoutConstant = iTimeout;
|
||||||
|
spw->ct.WriteTotalTimeoutMultiplier = iTimeout;
|
||||||
|
spw->ct.WriteTotalTimeoutConstant = 0;
|
||||||
|
|
||||||
|
if (!SetCommTimeouts (spw->hPort, &spw->ct)) {
|
||||||
|
ERR ("Unable to apply new timeout settings.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set baud rate
|
||||||
|
spw->dcb.BaudRate = uiPortSpeed;
|
||||||
|
if (!SetCommState (spw->hPort, &spw->dcb)) {
|
||||||
|
ERR ("Unable to apply new speed settings.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PurgeComm (spw->hPort, PURGE_RXABORT | PURGE_RXCLEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
uart_get_speed (const serial_port sp)
|
||||||
|
{
|
||||||
|
const serial_port_windows *spw = (serial_port_windows *) sp;
|
||||||
|
if (!GetCommState (spw->hPort, (serial_port) & spw->dcb))
|
||||||
|
return spw->dcb.BaudRate;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
|
||||||
|
{
|
||||||
|
if (!ReadFile (((serial_port_windows *) sp)->hPort, pbtRx, (DWORD)(*pszRx), (LPDWORD) pszRx, NULL)) {
|
||||||
|
return DEIO;
|
||||||
|
}
|
||||||
|
if (!*pszRx)
|
||||||
|
return DEIO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx)
|
||||||
|
{
|
||||||
|
DWORD dwTxLen = 0;
|
||||||
|
if (!WriteFile (((serial_port_windows *) sp)->hPort, pbtTx, szTx, &dwTxLen, NULL)) {
|
||||||
|
return DEIO;
|
||||||
|
}
|
||||||
|
if (!dwTxLen)
|
||||||
|
return DEIO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue