mercury-api-0.1.0.0: cbits/api/serial_transport_posix.c
/**
* @file serial_transport_posix.c
* @brief Mercury API - POSIX serial transport implementation
* @author Nathan Williams
* @date 10/20/2009
*/
/*
* Copyright (c) 2009 ThingMagic, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include "tm_reader.h"
#ifdef __APPLE__
#include <sys/ioctl.h>
#include <IOKit/serial/ioss.h>
#endif
#ifdef TMR_ENABLE_SERIAL_TRANSPORT_NATIVE
static TMR_Status
s_open(TMR_SR_SerialTransport *this)
{
int ret;
struct termios t;
#if TMR_MAX_READER_NAME_LENGTH > 0
TMR_SR_SerialPortNativeContext *c;
c = this->cookie;
c->handle = open(c->devicename, O_RDWR);
if (c->handle == -1)
return TMR_ERROR_COMM_ERRNO(errno);
#endif
/*
* Set 8N1, disable high-bit stripping, soft flow control, and hard
* flow control (modem lines).
*/
ret = tcgetattr(c->handle, &t);
if (-1 == ret)
return TMR_ERROR_COMM_ERRNO(errno);
t.c_iflag &= ~(ICRNL | IGNCR | INLCR | INPCK | ISTRIP | IXANY
| IXON | IXOFF | PARMRK);
t.c_oflag &= ~OPOST;
t.c_cflag &= ~(CRTSCTS | CSIZE | CSTOPB | PARENB);
t.c_cflag |= CS8 | CLOCAL | CREAD | HUPCL;
t.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 1;
ret = tcsetattr(c->handle, TCSANOW, &t);
if (-1 == ret)
return TMR_ERROR_COMM_ERRNO(errno);
return TMR_SUCCESS;
}
static TMR_Status
s_sendBytes(TMR_SR_SerialTransport *this, uint32_t length,
uint8_t* message, const uint32_t timeoutMs)
{
TMR_SR_SerialPortNativeContext *c;
int ret;
c = this->cookie;
do
{
ret = write(c->handle, message, length);
if (ret == -1)
{
if (ENXIO == errno)
{
return TMR_ERROR_TIMEOUT;
}
else
{
return TMR_ERROR_COMM_ERRNO(errno);
}
}
length -= ret;
message += ret;
}
while (length > 0);
return TMR_SUCCESS;
}
static TMR_Status
s_receiveBytes(TMR_SR_SerialTransport *this, uint32_t length,
uint32_t *messageLength, uint8_t* message, const uint32_t timeoutMs)
{
TMR_SR_SerialPortNativeContext *c;
int ret;
struct timeval tv;
fd_set set;
int status = 0;
*messageLength = 0;
c = this->cookie;
do
{
do
{
FD_ZERO(&set);
FD_SET(c->handle, &set);
tv.tv_sec = timeoutMs / 1000;
tv.tv_usec = (timeoutMs % 1000) * 1000;
/* Ideally should reset this timeout value every time through */
ret = select(c->handle + 1, &set, NULL, NULL, &tv);
} while (ret == -1 && errno == EINTR);
if (ret == -1)
{
return TMR_ERROR_COMM_ERRNO(errno);
}
if (ret < 1)
{
return TMR_ERROR_TIMEOUT;
}
ret = read(c->handle, message, length);
if (ret == -1)
{
if (ENXIO == errno)
{
return TMR_ERROR_TIMEOUT;
}
else
{
return TMR_ERROR_COMM_ERRNO(errno);
}
}
if (0 == ret)
{
/**
* We should not be here, coming here means the select()
* is success , but we are not able to read the data.
* check the serial port connection status.
**/
ret = ioctl(c->handle, TIOCMGET, &status);
if (-1 == ret)
{
/* not success. check for errno */
if (EIO == errno)
{
/**
* EIO means I/O error, may serial port got disconnected,
* throw the error.
**/
return TMR_ERROR_TIMEOUT;
}
}
}
length -= ret;
*messageLength += ret;
message += ret;
}
while (length > 0);
return TMR_SUCCESS;
}
static TMR_Status
s_setBaudRate(TMR_SR_SerialTransport *this, uint32_t rate)
{
TMR_SR_SerialPortNativeContext *c;
c = this->cookie;
#if defined(__APPLE__)
{
speed_t speed = rate;
if (ioctl(c->handle, IOSSIOSPEED, &speed) == -1)
return TMR_ERROR_COMM_ERRNO(errno);
}
#else
{
struct termios t;
tcgetattr(c->handle, &t);
#define BCASE(t,n) case n: cfsetispeed((t),B##n); cfsetospeed((t),B##n); break;
switch (rate)
{
BCASE(&t, 9600);
BCASE(&t, 19200);
BCASE(&t, 38400);
// Believe it or not, speeds beyond 38400 aren't required by POSIX.
#ifdef B57600
BCASE(&t, 57600);
#endif
#ifdef B115200
BCASE(&t, 115200);
#endif
#ifdef B230400
BCASE(&t, 230400);
#endif
#ifdef B460800
BCASE(&t, 460800);
#endif
#ifdef B921600
BCASE(&t, 921600);
#endif
default:
return TMR_ERROR_INVALID;
}
#undef BCASE
if (tcsetattr(c->handle, TCSANOW, &t) != 0)
{
return TMR_ERROR_COMM_ERRNO(errno);
}
}
#endif /* __APPLE__ */
return TMR_SUCCESS;
}
static TMR_Status
s_shutdown(TMR_SR_SerialTransport *this)
{
TMR_SR_SerialPortNativeContext *c;
c = this->cookie;
close(c->handle);
/* What, exactly, would be the point of checking for an error here? */
return TMR_SUCCESS;
}
static TMR_Status
s_flush(TMR_SR_SerialTransport *this)
{
TMR_SR_SerialPortNativeContext *c;
c = this->cookie;
if (tcflush(c->handle, TCOFLUSH) == -1)
{
return TMR_ERROR_COMM_ERRNO(errno);
}
return TMR_SUCCESS;
}
TMR_Status
TMR_SR_SerialTransportNativeInit(TMR_SR_SerialTransport *transport,
TMR_SR_SerialPortNativeContext *context,
const char *device)
{
#if TMR_MAX_READER_NAME_LENGTH > 0
if (strlen(device) + 1 > TMR_MAX_READER_NAME_LENGTH)
{
return TMR_ERROR_INVALID;
}
strcpy(context->devicename, device);
#else
/* No space to store the device name, so open it now */
context->handle = open(device, O_RDWR);
if (context->handle == -1)
{
return TMR_ERROR_COMM_ERRNO(errno);
}
#endif
transport->cookie = context;
transport->open = s_open;
transport->sendBytes = s_sendBytes;
transport->receiveBytes = s_receiveBytes;
transport->setBaudRate = s_setBaudRate;
transport->shutdown = s_shutdown;
transport->flush = s_flush;
return TMR_SUCCESS;
}
#endif