RS 232 Programming

From: WahJava (wahjava_at_gmail.com)
Date: 07/08/05


Date: 8 Jul 2005 05:47:41 -0700

Hi hackers,

I'm writing a simple file transfer program for transferring files
between two computers over Null Modem Cable. I've written two programs
for this purpose:

1. dumber - Used to recieve a file
2. dumper - Used to send a file

I'm assuming 16550A UART (do I need to specify this), 115200 bps
(max. speed of 16550A), 8bit, Parity none, Hardware Flow Control.

Initially, I've coded this stuff in Microsoft C/C++ in Windows, which
worked fine. Then I've created a GNU/Linux version of the dumper which
worked fine. But my GNU/Linux version of the dumber recieved corrupted
files (I've tested by transferring GZipped file).

I'm a newbie in RS232 world. I've followed the document "Interfacing
the Serial / RS232 Port V5.0" available from:
http://www.senet.com.au/~cpeacock.

I'm using "Low-Level Terminal Interface" which is documented in "GNU C
Library Manual" for communication in GNU/Linux. I hope I'm using the
correct way.

Are there any other (recommended) way of communication over RS232 ?

I'm running Fedora Core III and Windows XP SP2 on Pentium 4 Processor.

What I'm observing is, when my Windows box sends 14400 bytes, my
GNU/Linux receives data in chunks of 16 bytes, 12 bytes etc.. Why is
it so ? Is there any way to set buffer sizes for the serial ports ?

I've tested out this code in FreeBSD 5.4 beta 3 (Sept. 2004), dumper
worked fine. But what I'm confused with is the naming of devices in
Unix ? The /dev/cuaxxx has two more similar named device files
/dev/cuaiaxxx and /dev/cualaxxxx (spellings might not be correct),
what are they used for. Is there any map of device names with their
corresponding device files with little bit detail, for FreeBSD and
Linux, available anywhere ?

I don't have regular access to Internet so I might not respond on
time.

Thanx in advance for your support,

Your admirer,
Ashish Shukla alias Wah Java !!

Wah Java !!

(http://www.geocities.com/wah_java_dotnet/index.html)

(Code attached)

/*************************************************
 * dumber.c - Windows Version
 *************************************************/

#include "windefines.h"
#include <stdio.h>

/* For command-line arguments */
#ifdef UNICODE
#define argv lpArgvW
#else
#define argv args
#endif

#ifdef UNICODE
static LPWSTR lpwCmdLine = NULL;
static LPWSTR* lpArgvW = NULL;
#endif

#define BUFSIZE 14400
#define dump_error() __dump_error(GetLastError(), __TFILE__, __LINE__)

static void print_usage(LPTSTR);
static void cleanup(void);
static void __dump_error(DWORD, LPCTSTR, DWORD);

#ifndef min
#define min(x,y) ((x) < (y) ? (x) : (y))
#endif

int _tmain(int argc, char** args)
{
  HANDLE hCOM = NULL;
  HANDLE hFile = NULL;
  HANDLE hHeap = NULL;
  LPVOID lpBuff = NULL;
  COMMCONFIG config;
  DWORD dwFileSize;
  DWORD dwSize;
  DWORD dwStart;
  DWORD dwEnd;

#ifdef UNICODE
  lpwCmdLine = GetCommandLineW();
  lpArgvW = CommandLineToArgvW(lpwCmdLine, &argc);
#endif

  hHeap = GetProcessHeap();

  if(argc < 3)
          print_usage(argv[0]);

  if((hFile = CreateFile(argv[2], GENERIC_WRITE, 0,
                                                 NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
                                                 NULL)) == INVALID_HANDLE_VALUE)
        dump_error();

  if((hCOM = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ,
                                                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                                NULL)) == INVALID_HANDLE_VALUE)
        dump_error();

  SetupComm(hCOM, BUFSIZE, BUFSIZE);

  if(!(lpBuff = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, BUFSIZE)))
        dump_error();

  ZeroMemory(&config, dwSize = sizeof(config));

  GetDefaultCommConfig(argv[1], &config, &dwSize);

  config.dcb.BaudRate = CBR_115200;

  SetCommState(hCOM, &config.dcb);

  if(!ReadFile(hCOM, &dwFileSize, sizeof(DWORD), &dwSize, NULL))
        dump_error();
  _tprintf(_T("File size is %i bytes read %i bytes.\r\n"), dwFileSize,
dwSize);

  dwStart = GetTickCount();

  while(ReadFile(hCOM, lpBuff, min(dwFileSize, BUFSIZE), &dwSize, NULL)
&& dwFileSize)
        {
          if(!dwSize)
                break; /* EOF Reached */
          _tprintf(_T("Read: %07i bytes, "), dwSize);
          dwFileSize -= dwSize;
          WriteFile(hFile, lpBuff, dwSize, &dwSize, NULL);
          _tprintf(_T("Written: %07i bytes.\r\n"), dwSize);

          if(!dwFileSize)
                break;
        }
  dwEnd = GetTickCount();

  HeapFree(hHeap, 0, lpBuff);

  FlushFileBuffers(hFile);
  CloseHandle(hCOM);
  CloseHandle(hFile);

  _tprintf(_T("%i ms.\r\n"), dwEnd - dwStart);

  cleanup();

  return 0;
}

void print_usage(LPTSTR lpProgName)
{
  _tprintf(_T("Usage: %s [serialport] [filename]\r\n"), lpProgName);
  cleanup();
}

void cleanup(void)
{
  if(lpArgvW)
        {
          lpArgvW = NULL;
          GlobalFree(lpArgvW);
        }

  ExitProcess(0);
}

void __dump_error(DWORD dwError, LPCTSTR lpszFile, DWORD dwLine)
{
  _ftprintf(stderr, _T("Error %i in \"%s\" at line no. %i\r\n"),
                        dwError, lpszFile, dwLine);
  cleanup();
}

/*************************************************
 * dumper.c - Windows Version
 *************************************************/

#include "windefines.h"
#include <stdio.h>

/* For command-line arguments */
#ifdef UNICODE
#define argv lpArgvW
#else
#define argv args
#endif

#ifdef UNICODE
static LPWSTR lpwCmdLine = NULL;
static LPWSTR* lpArgvW = NULL;
#endif

#define BUFSIZE 14400
#define dump_error() __dump_error(GetLastError(), __TFILE__, __LINE__)

static void print_usage(LPTSTR);
static void cleanup(void);
static void __dump_error(DWORD, LPCTSTR, DWORD);

int _tmain(int argc, char** args)
{
  HANDLE hCOM = NULL;
  HANDLE hFile = NULL;
  HANDLE hHeap = NULL;
  LPVOID lpBuff = NULL;
  COMMCONFIG config;
  DWORD dwSize;
  BY_HANDLE_FILE_INFORMATION file_info;
  DWORD dwStart;
  DWORD dwEnd;

#ifdef UNICODE
  lpwCmdLine = GetCommandLineW();
  lpArgvW = CommandLineToArgvW(lpwCmdLine, &argc);
#endif

  hHeap = GetProcessHeap();

  if(argc < 3)
          print_usage(argv[0]);

  if((hFile = CreateFile(argv[2], GENERIC_READ, FILE_SHARE_READ,
                                                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                                 NULL)) == INVALID_HANDLE_VALUE)
        dump_error();

  if((hCOM = CreateFile(argv[1], GENERIC_WRITE, FILE_SHARE_READ,
                                                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                                NULL)) == INVALID_HANDLE_VALUE)
        dump_error();

  SetupComm(hCOM, BUFSIZE, BUFSIZE);

  if(!(lpBuff = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, BUFSIZE)))
        dump_error();

  ZeroMemory(&config, dwSize = sizeof(config));
  ZeroMemory(&file_info, sizeof(BY_HANDLE_FILE_INFORMATION));

  GetDefaultCommConfig(argv[1], &config, &dwSize);
  GetFileInformationByHandle(hFile, &file_info);

  _tprintf(_T("File Size: %i bytes.\r\n"), file_info.nFileSizeLow);

  config.dcb.BaudRate = CBR_115200;

  SetCommState(hCOM, &config.dcb);
  WriteFile(hCOM, &file_info.nFileSizeLow,
sizeof(file_info.nFileSizeLow),
                        &dwSize, NULL);

  _tprintf(_T("Written %i bytes.\r\n"), dwSize);

  dwStart = GetTickCount();

  while(ReadFile(hFile, lpBuff, BUFSIZE, &dwSize, NULL))
        {
          if(!dwSize)
                break; /* EOF Reached */
          _tprintf(_T("Read: %07i bytes, "), dwSize);
          WriteFile(hCOM, lpBuff, dwSize, &dwSize, NULL);
          _tprintf(_T("Written: %07i bytes.\r\n"), dwSize);
        }

  dwEnd = GetTickCount();

  HeapFree(hHeap, 0, lpBuff);

  CloseHandle(hCOM);
  CloseHandle(hFile);

  _tprintf(_T("%i ms.\r\n"), dwEnd - dwStart);

   cleanup();

  return 0;
}

void print_usage(LPTSTR lpProgName)
{
  _tprintf(_T("Usage: %s [comport] [filename]\r\n"), lpProgName);
  cleanup();
}

void cleanup(void)
{
  if(lpArgvW)
        {
          lpArgvW = NULL;
          GlobalFree(lpArgvW);
        }

  ExitProcess(0);
}

void __dump_error(DWORD dwError, LPCTSTR lpszFile, DWORD dwLine)
{
  _ftprintf(stderr, _T("Error %i in \"%s\" at line no. %i\r\n"),
                        dwError, lpszFile, dwLine);
  cleanup();
}

/***********************************
 * windefines.h - Windows Version
 ***********************************/
#ifndef __win_de_fines_dot_h
#define __win_de_fines_dot_h

/* Unicode can be enabled/disabled from here */
#ifndef UNICODE
#define UNICODE
#endif

#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif
#else
#ifdef _UNICODE
#undef _UNICODE
#endif
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif

/* Maintain a consistency */
#ifndef WINVER
#define WINVER _WIN32_WINNT
#else
#undef WINVER
#define WINVER _WIN32_WINNT
#endif

#ifndef WIN32
#define WIN32
#endif

#ifdef UNICODE
#define __TFILE__ _T(__FILE__)
#else
#define __TFILE__ __FILE__
#endif

#include <tchar.h>
#include <windows.h>

#endif

/*******************************
 * dumper.c - GNU/Linux Version
 *******************************/

/* -*- mode: c; mode: auto-fill; tab-width: 4 -*- */
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>

#include <string.h>
#include <strings.h>
#include <stdlib.h>

#include <errno.h>

#ifndef min
#define min(x,y) (((x) < (y)) ? (x):(y))
#endif

#define BUFSIZE 14400
#define dump_error() __dump_error(errno,__FILE__,__LINE__)

static void __dump_error(int, const char*, int);
static int filelength(int);

int main(int argc, char** argv)
{
  int fd_file, fd_tty;
  int filelen;
  int i;
  struct termios tmi;
  char* buf = NULL;

  if(argc < 3)
    {
      printf("Usage: %s [/dev/ttySx] [file]\n", argv[0]);
      return 0;
    }

  if((fd_file = open(argv[2], O_RDONLY)) == -1)
    dump_error();

  if((fd_tty = open(argv[1], O_WRONLY)) == -1)
    dump_error();

  if(!isatty(fd_tty))
    {
      close(fd_file);
      close(fd_tty);
      fprintf(stderr, "Error: %s is not a terminal\n", argv[1]);
      return 1;
    }

  buf = malloc(BUFSIZE);
  if(!buf)
    dump_error();

  bzero(&tmi, sizeof(struct termios));

  /* Retrieve current attributes */
  if(tcgetattr(fd_tty, &tmi) == -1)
    dump_error();

  /* No Parity Check / No XON/XOFF Flow Control / No stripping */
  tmi.c_iflag &= ~(INPCK | IGNPAR | PARMRK | IGNBRK | BRKINT | ISTRIP
                   | IXON | IXOFF);
  tmi.c_oflag &= ~OPOST; /* No Post Output Modifications */
  tmi.c_cflag &= ~(PARENB | CSIZE | CSTOPB); /* 1 stopbit */
  tmi.c_cflag |= CS8 | CRTSCTS; /* 8 bpB / CTS/RTS Flow Control */
  /* Non Canonical / No Signal process / No Echo */
  tmi.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL );

  /* But do we, need this, port is opened in write only mode */
  tmi.c_cc[VMIN] = 1; /* Minimum 1 bytes should be read */
  tmi.c_cc[VTIME] = 10; /* Timeout 1s */

  /* Set output speed 115200 bps,
   * Note: The speed is set in structure only, so it is not effective
yet
   * which means we've to apply changes after setting speed, otherwise
   * this still transmits with old speed.
   */
  if(cfsetospeed(&tmi, B115200) == -1)
    dump_error();

  /* Apply now */
  if(tcsetattr(fd_tty, TCSANOW, &tmi) == -1)
    dump_error();

  filelen = filelength(fd_file);

  printf("File Size: %i bytes.\n", filelen);

  while(filelen)
    {
      i = read(fd_file, buf, min(filelen, BUFSIZE));
      printf("Read %7i bytes ", i);
      printf("Wrote %7i bytes.\n", write(fd_tty, buf, i));

      if(filelen < i)
                filelen = 0;
      else
                filelen -= i;
    }

  free(buf);
  close(fd_tty);
  close(fd_file);

  return 0;
}

void __dump_error(int err, const char* file, int line)
{
  fprintf(stderr, "Error %i: %s\nFile: %s, Line: %i\n", err,
strerror(err),
          file, line);

  exit(1);
}

int filelength(int fd)
{
  struct stat fs;

  if(fstat(fd, &fs) == -1)
    dump_error();

  return fs.st_size;
}
/*
  vim:tw=78:ts=4
*/

/*******************************
 * dumber.c - GNU/Linux Version
 *******************************/

/* -*- mode: c; mode: auto-fill; tab-width: 4 -*- */
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>

#include <string.h>
#include <strings.h>
#include <stdlib.h>

#include <errno.h>

#ifndef min
#define min(x,y) (((x) < (y)) ? (x):(y))
#endif

#define BUFSIZE 14400
#define dump_error() __dump_error(errno,__FILE__,__LINE__)

static void __dump_error(int, const char*, int);

int main(int argc, char** argv)
{
  int fd_file, fd_tty;
  int filelen;
  int i;
  struct termios tmi;
  struct termios tmi2;
  struct timeval tiv;
  fd_set fs;
  char* buf = NULL;

  if(argc < 3)
    {
      printf("Usage: %s [/dev/ttySx] [file]\n", argv[0]);
      return 0;
    }

  if((fd_file = open(argv[2], (O_WRONLY | O_CREAT | O_EXCL),
                     (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP))) == -1)
    dump_error();

  if((fd_tty = open(argv[1], O_RDONLY )) == -1)
    dump_error();

  if(!isatty(fd_tty))
    {
      close(fd_file);
      close(fd_tty);
      fprintf(stderr, "Error: %s is not a terminal\n", argv[1]);
      return 1;
    }

  buf = malloc(BUFSIZE);
  if(!buf)
    dump_error();

  bzero(&tmi, sizeof(struct termios));

  /* Retrieve current attributes */
  if(tcgetattr(fd_tty, &tmi) == -1)
    dump_error();

  memcpy(&tmi2, &tmi, sizeof(tmi));

  /* No Parity Check / No XON/XOFF Flow Control / No stripping */
  tmi.c_iflag &= ~(INPCK | IGNPAR | PARMRK | IGNBRK | BRKINT | ISTRIP
                   | IXON | IXOFF);
  tmi.c_oflag &= ~OPOST; /* No Post Output Modifications */
  tmi.c_cflag &= ~(PARENB | CSIZE | CSTOPB); /* 1 stopbit */
  tmi.c_cflag |= CS8 | CRTSCTS; /* 8 bpB / CTS/RTS Flow Control */
  /* Non Canonical / No Signal process / No Echo */
  tmi.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL );

  /* This is an array of bytes hmmm... */
  tmi.c_cc[VMIN] = 1; /* Minimum 1 bytes should be read */
  tmi.c_cc[VTIME] = 0; /* Timeout 1s */

  /* Set input speed 115200 bps,
   * Note: The speed is set in structure only, so it is not effective
yet
   * which means we've to apply changes after setting speed, otherwise
   * this still transmits with old speed.
   */
  if(cfsetispeed(&tmi, B115200) == -1)
    dump_error();

  /* Apply now */
  if(tcsetattr(fd_tty, TCSANOW, &tmi) == -1)
    dump_error();

  i = read(fd_tty, &filelen, sizeof(filelen));

  printf("File Length: %i, %i bytes\n", filelen, i);

  while(filelen)
    {
          i = read(fd_tty, buf, min(BUFSIZE, filelen));

      printf("Read %7i bytes ", i);
      printf("Wrote %7i bytes.\n", write(fd_file, buf, i));

      if(filelen < i)
                filelen = 0;
      else
                filelen -= i;
    }

  tcsetattr(fd_tty, TCSANOW, &tmi2);

  free(buf);
  close(fd_tty);
  fsync(fd_file);
  close(fd_file);

  return 0;
}

void __dump_error(int err, const char* file, int line)
{
  fprintf(stderr, "Error %i: %s\nFile: %s, Line: %i\n", err,
strerror(err),
          file, line);
  
  exit(1);
}

/*
  vim:tw=78:ts=4
*/



Relevant Pages