packages feed

mercury-api-0.1.0.0: cbits/api/serial_transport_win32.c

/**
 *  @file serial_transport_win32.c
 *  @brief Mercury API - Serial transport over local serial port on Win32
 *  @author Nathan Williams
 *  @date 10/20/2009
 */


/*
 * Copyright (c) 2010 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 "tm_config.h"
#include "tmr_status.h"

#if defined(WIN32) || defined(WINCE)
#if !defined(_WINSOCK2API_) && !defined(_WINSOCKAPI_)
#include <winsock2.h>
#endif
#include <stdio.h>

#  if defined(WINCE)
#    define snprintf _snprintf
#  elif !defined(__GNUC__)
#    define snprintf sprintf_s
#  endif

#endif /* WIN32 */

#include "tm_reader.h"

__declspec(dllexport) TMR_Status
s_open(TMR_SR_SerialTransport *this)
{
  TMR_SR_SerialPortNativeContext *c;
  COMMTIMEOUTS timeOuts;
  DCB dcb;

  c = this->cookie;
  
  c->handle = CreateFile((TCHAR*)c->devicename,
                         GENERIC_READ | GENERIC_WRITE,
                         0,             /* not shared */
                         NULL,          /* default security */
                         OPEN_EXISTING, /* don't create a new file */
                         0,             /* flags and attributes */
                         NULL);         /* template file */
                         
  if (INVALID_HANDLE_VALUE == c->handle)
    return TMR_ERROR_INVALID; /* @todo use GetLastError() */

  timeOuts.ReadIntervalTimeout          = 0xFFFFFFFF;
  timeOuts.ReadTotalTimeoutConstant     = 0;
  timeOuts.ReadTotalTimeoutMultiplier   = 0;
  timeOuts.WriteTotalTimeoutConstant    = 5000;
  timeOuts.WriteTotalTimeoutMultiplier  = 0;
  
  SetCommTimeouts(c->handle, &timeOuts);

  dcb.DCBlength = sizeof(DCB);
  GetCommState(c->handle, &dcb);
  dcb.fOutxCtsFlow = 0;
  dcb.fOutxDsrFlow = 0;
  dcb.fDtrControl = DTR_CONTROL_DISABLE;
  dcb.fDsrSensitivity = 0;
  dcb.fOutX = 0;
  dcb.fInX = 0;
  dcb.fNull = 0;
  dcb.fRtsControl = RTS_CONTROL_DISABLE;
  dcb.ByteSize  = 8;
  dcb.fParity = NOPARITY;
  dcb.StopBits = ONESTOPBIT;
  if ((0 == SetCommState(c->handle, &dcb)) ||
      (0 == SetupComm(c->handle, 10000, 10000)))
  {
    return TMR_ERROR_INVALID;  /* @todo use GetLastError() */
  }
  
  return TMR_SUCCESS;
}

__declspec(dllexport) TMR_Status
s_sendBytes(TMR_SR_SerialTransport *this, uint32_t length,
            uint8_t* message, const uint32_t timeoutMs)
{
  TMR_SR_SerialPortNativeContext *c;
  BOOL writeStatus;
  COMMTIMEOUTS timeOuts;

  c = this->cookie;
  GetCommTimeouts(c->handle, &timeOuts);
  timeOuts.WriteTotalTimeoutConstant = timeoutMs;
  SetCommTimeouts(c->handle, &timeOuts);
  
  {
	long numberOfBytesWritten;
	writeStatus = WriteFile(c->handle, message, length, &numberOfBytesWritten, NULL);
  }
  if (0 == writeStatus)
  {
    return TMR_ERROR_TIMEOUT;  /* @todo use GetLastError() */
  }
  return TMR_SUCCESS;
}

__declspec(dllexport) TMR_Status
s_receiveBytes(TMR_SR_SerialTransport *this, uint32_t length,
              uint32_t* messageLength, uint8_t* message, const uint32_t
timeoutMs)
{
 DWORD readLength;
 DWORD errorFlags;
 COMSTAT comStat;
 TMR_SR_SerialPortNativeContext *c;
 BOOL    readStatus;
 COMMTIMEOUTS timeOuts;

 c = this->cookie;
 *messageLength=0;

 GetCommTimeouts(c->handle, &timeOuts);
 timeOuts.ReadTotalTimeoutConstant = timeoutMs;
 SetCommTimeouts(c->handle, &timeOuts);

 ClearCommError(c->handle, &errorFlags, &comStat);
 while (length > 0)
 {
   readLength=0;
   readStatus = ReadFile(c->handle, message, length, &readLength, NULL);
   *messageLength += readLength;
   if (0 == readStatus)
   {
     return TMR_ERROR_TIMEOUT;  /* @todo use GetLastError() */
   }
   if(readLength == 0)
   {
     return TMR_ERROR_TIMEOUT;
   }
   length -= readLength;
   message += readLength;

 }
 return TMR_SUCCESS;
}


__declspec(dllexport) TMR_Status
s_setBaudRate(TMR_SR_SerialTransport *this, uint32_t rate)
{
  TMR_SR_SerialPortNativeContext *c;
  DCB   dcb;

  c = this->cookie;

  dcb.DCBlength = sizeof(DCB);
  GetCommState(c->handle, &dcb);
  dcb.BaudRate  = rate;

  if (0 == SetCommState(c->handle, &dcb)) /* @todo use GetLastError() */
  {
    return TMR_ERROR_INVALID;
  }

  return TMR_SUCCESS;
}

static TMR_Status
s_shutdown(TMR_SR_SerialTransport *this)
{
  TMR_SR_SerialPortNativeContext *c;

  c = this->cookie;

  CloseHandle(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 (PurgeComm(c->handle, PURGE_RXCLEAR) == 0)
  {
	  return TMR_ERROR_COMM_ERRNO(errno);
  }
  return TMR_SUCCESS;
}

__declspec(dllexport)TMR_Status
TMR_SR_SerialTransportNativeInit(TMR_SR_SerialTransport *transport,
                                 TMR_SR_SerialPortNativeContext *context,
                                 const char *device)
{
  // Transform COM port name from "/COMnn" to "\\\\.\\COMnn"
  if (strlen(device)-1 + 4 + 1 > TMR_MAX_READER_NAME_LENGTH)
  {
    return TMR_ERROR_INVALID;
  }
  snprintf(context->devicename, sizeof(context->devicename),
    "\\\\.\\%s", device+1);

  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;
}