Logo Search packages:      
Sourcecode: cbmlink version File versions  Download package

serial.c

Go to the documentation of this file.
/**
 * @file serial.c
 * Serial transfer routines
 * @author Marko Mäkelä (msmakela@nic.funet.fi)
 */

/*
 * Copyright © 2001,2002 Marko Mäkelä.
 * 
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 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 General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef COMM_SERIAL
# if defined __unix || defined __unix__ || defined __APPLE__
#  include <termios.h>
#  include <unistd.h>
#  include <fcntl.h>
# endif /* defined __unix || defined __unix__ || defined __APPLE__ */

# if defined __WIN32 || defined __WIN32__
#  include <windows.h>
#  ifndef WIN32
#   define WIN32
#  endif /* WIN32 */
# endif /* defined __WIN32 || defined __WIN32__ */

# if defined __AMIGA__
#  include <proto/exec.h>
#  include <devices/serial.h>
#  include <dos/dos.h>
#  include <clib/alib_protos.h>
#  include <signal.h>
# endif /* __AMIGA__ */

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>

# include "info.h"
# include "serial.h"

# ifdef WIN32
/** file handle for the serial device (0=none) */
static HANDLE serialfd = 0;
/** previous terminal input/output settings for the serial line */
static DCB tio;
/** previous timeout settings for the serial line */
static COMMTIMEOUTS tio2;
/** new terminal input/output settings for the serial line */
static DCB newtio;
/** new timeout settings for the serial line */
static COMMTIMEOUTS newtio2;
# elif defined __AMIGA__
/** extended input/output request structure */
static struct IOExtSer ser;

/** Flush the input/output operation on the serial line
 * @return  the WaitIO exit status
 */
static BYTE
flush_serial (void)
{
  const long waitsigs = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D |
    1L << ser.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
  for (;;) {
    if (CheckIO ((struct IORequest*) &ser))
      return WaitIO ((struct IORequest*) &ser);
    if (Wait (waitsigs) & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)) {
      AbortIO ((struct IORequest*) &ser);
      WaitIO ((struct IORequest*) &ser);
      raise (SIGINT);
    }
  }
}
# else /* !WIN32 && !AMIGA */
/** file descriptor for the serial device (0=none) */
static int serialfd = 0;
/** previous terminal input/output settings for the serial line */
static struct termios tio;
/** new terminal input/output settings for the serial line */
static struct termios newtio;
# endif /* !WIN32 && !AMIGA */

/** Read computer information
 * @param hostinfo      (output) the computer model information
 * @return        zero on success, nonzero on failure
 */
static int
serial_detect (struct hostinfo* hostinfo)
{
  unsigned char detectbuf[5];
  if (serial_write ("", 1))
    return 1;
  if (serial_read (detectbuf, sizeof detectbuf))
    return 2;
  hostinfo->host = detectbuf[0];
  hostinfo->driver = (unsigned) detectbuf[1] | (unsigned) detectbuf[2] << 8;
  hostinfo->basic = (unsigned) detectbuf[3] | (unsigned) detectbuf[4] << 8;
  return 0;
}

/** Open the communication channel
 * @param dev           name of the communication device
 * @param hostinfo      (output) the computer model information
 * @return        zero on success, nonzero on failure
 */
int
serial_init (const char* dev, struct hostinfo* hostinfo)
{
  char* endp;
  unsigned long sp = strtoul (dev, &endp, 0);
  if (endp > dev && *endp++ != ',') {
    fprintf (stderr, "serial_init %s: expecting [speed,]device\n",
           dev);
    return 1;
  }
  if (endp == dev)
    sp = 19200; /* the default speed */

# ifdef WIN32
  if ((serialfd = CreateFile (endp, GENERIC_WRITE | GENERIC_READ, 0, 0,
                        OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
    report_error ("serial_init: CreateFile", endp);
    return 1;
  }

  if (!GetCommState (serialfd, &tio)) {
    report_error ("serial_init: GetCommState", endp);
  init_fail:
    CloseHandle (serialfd);
    serialfd = INVALID_HANDLE_VALUE;
    return 2;
  }
  else if (!GetCommTimeouts (serialfd, &tio2)) {
    report_error ("serial_init: GetCommTimeouts", endp);
    goto init_fail;
  }
# elif defined __AMIGA__
  memset (&ser, 0, sizeof ser);
  ser.IOSer.io_Message.mn_ReplyPort = (struct MsgPort*) CreatePort (0, 0);
  ser.io_SerFlags = SERF_RAD_BOOGIE | SERF_XDISABLED;
  if (OpenDevice ((const unsigned char*) dev, 0,
              (struct IORequest*) &ser, 0)) {
    fprintf (stderr, "serial_init %s: OpenDevice returned %d\n",
           dev, ser.IOSer.io_Error);
    serial_close ();
    return 1;
  }
# else /* !WIN32 && !AMIGA */
  if ((serialfd = open (endp, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
    fputs ("serial_init ", stderr);
    fputs (endp, stderr);
    perror (": open");
    return 1;
  }
  else {
    int flags = fcntl (serialfd, F_GETFL);
    if (flags < 0) {
      fputs ("serial_init ", stderr);
      fputs (endp, stderr);
      perror (": fcntl(GETFL)");
      close (serialfd);
      serialfd = -1;
      return 2;
    }
    else if (flags & O_NONBLOCK &&
           fcntl (serialfd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
      fputs ("serial_init ", stderr);
      fputs (endp, stderr);
      perror (": fcntl(SETFL)");
      close (serialfd);
      serialfd = -1;
      return 2;
    }
  }

  if (tcgetattr (serialfd, &tio)) {
    fputs ("serial_init ", stderr);
    fputs (endp, stderr);
    perror (": tcgetattr");
  init_fail:
    close (serialfd);
    serialfd = -1;
    return 2;
  }
# endif /* !WIN32 && !AMIGA */

  /* set the communication parameters */
# ifdef WIN32
  memcpy (&newtio, &tio, sizeof newtio);
  memcpy (&newtio2, &tio2, sizeof newtio2);
  switch (sp) {
  case 300:
    newtio.BaudRate = CBR_300;
    break;
  case 600:
    newtio.BaudRate = CBR_600;
    break;
  case 1200:
    newtio.BaudRate = CBR_1200;
    break;
  case 2400:
    newtio.BaudRate = CBR_2400;
    break;
  case 4800:
    newtio.BaudRate = CBR_4800;
    break;
  case 9600:
    newtio.BaudRate = CBR_9600;
    break;
  case 19200:
    newtio.BaudRate = CBR_19200;
    break;
  case 38400:
    newtio.BaudRate = CBR_38400;
    break;
  default:
    fputs ("serial_init ", stderr);
    fputs (dev, stderr);
    fputs (": unsupported speed\n", stderr);
    goto init_fail;
  }
  newtio.fBinary = 1;
  newtio.fParity = newtio.fOutxCtsFlow = newtio.fOutxDsrFlow = 0;
  newtio.fDtrControl = DTR_CONTROL_DISABLE;
  newtio.fDsrSensitivity = 0;
  newtio.fOutX = 0, newtio.fInX = 0;
  newtio.fErrorChar = newtio.fNull = 0;
  newtio.fRtsControl = RTS_CONTROL_DISABLE;
  newtio.fAbortOnError = 0;
  newtio.ByteSize = 8;
  newtio.Parity = NOPARITY;
  newtio.StopBits = ONESTOPBIT;
  newtio.XonChar = newtio.XoffChar = 0;

  newtio2.ReadIntervalTimeout = 1000; /* 1 second */
  newtio2.ReadTotalTimeoutConstant = 1000; /* 1 second */
  newtio2.ReadTotalTimeoutMultiplier =
    newtio2.WriteTotalTimeoutMultiplier =
    newtio2.WriteTotalTimeoutConstant = 0;

  PurgeComm (serialfd, PURGE_TXCLEAR | PURGE_RXCLEAR);
  if (!SetCommState (serialfd, &newtio)) {
    report_error ("serial_init: SetCommState", endp);
    goto init_fail;
  }
  else if (!SetCommTimeouts (serialfd, &newtio2)) {
    report_error ("serial_init: SetCommTimeouts", endp);
    SetCommState (serialfd, &tio);
    goto init_fail;
  }
# elif defined __AMIGA__
  ser.IOSer.io_Command = SDCMD_SETPARAMS;
  ser.io_ExtFlags = 0;
  ser.io_SerFlags = SERF_RAD_BOOGIE | SERF_XDISABLED;
  ser.io_Baud = sp;
  if (DoIO ((struct IORequest*) &ser)) {
    fprintf (stderr, "serial_init: DoIO (SDCMD_SETPARAMS) returned %d\n",
           ser.IOSer.io_Error);
    return 2;
  }
# else /* !WIN32 && !AMIGA */
  memcpy (&newtio, &tio, sizeof newtio);
  newtio.c_iflag = IGNBRK | IGNPAR;
  newtio.c_oflag = 0;
  newtio.c_cflag = CS8 | CLOCAL | CREAD;
  switch (sp) {
  case 300:
    cfsetispeed (&newtio, B300), cfsetospeed (&newtio, B300);
    break;
  case 600:
    cfsetispeed (&newtio, B600), cfsetospeed (&newtio, B600);
    break;
  case 1200:
    cfsetispeed (&newtio, B1200), cfsetospeed (&newtio, B1200);
    break;
  case 2400:
    cfsetispeed (&newtio, B2400), cfsetospeed (&newtio, B2400);
    break;
  case 4800:
    cfsetispeed (&newtio, B4800), cfsetospeed (&newtio, B4800);
    break;
  case 9600:
    cfsetispeed (&newtio, B9600), cfsetospeed (&newtio, B9600);
    break;
  case 19200:
    cfsetispeed (&newtio, B19200), cfsetospeed (&newtio, B19200);
    break;
  case 38400:
    cfsetispeed (&newtio, B38400), cfsetospeed (&newtio, B38400);
    break;
  default:
    fputs ("serial_init ", stderr);
    fputs (dev, stderr);
    fputs (": unsupported speed\n", stderr);
    goto init_fail;
  }
  newtio.c_lflag = 0;
  memset (newtio.c_cc, 0, sizeof newtio.c_cc);
  newtio.c_cc[VMIN] = 10; /* read at least 10 chars */
  newtio.c_cc[VTIME] = 10; /* 1 second timeout */

  /* flush the input and output buffers */
  tcflush (serialfd, TCIOFLUSH);

  if (tcsetattr (serialfd, TCSANOW, &newtio)) {
    fputs ("serial_init ", stderr);
    fputs (endp, stderr);
    perror (": tcsetattr");
    close (serialfd);
    serialfd = -1;
    return 2;
  }
# endif /* !WIN32 && !AMIGA */

  return serial_detect (hostinfo);
}

/** Close the communication channel */
void
serial_close (void)
{
# ifdef WIN32
  if (serialfd && serialfd != INVALID_HANDLE_VALUE) {
    SetCommState (serialfd, &tio);
    SetCommTimeouts (serialfd, &tio2);
    CloseHandle (serialfd);
    serialfd = 0;
  }
# elif defined __AMIGA__
  if (ser.IOSer.io_Device)
    CloseDevice ((struct IORequest*) &ser);
  if (ser.IOSer.io_Message.mn_ReplyPort)
    DeletePort (ser.IOSer.io_Message.mn_ReplyPort);
  memset (&ser, 0, sizeof ser);
# else /* !WIN32 && !AMIGA */
  if (serialfd > 0) {
    tcsetattr (serialfd, TCSANOW, &tio);
    close (serialfd);
    serialfd = 0;
  }
# endif /* !WIN32 && !AMIGA */
}

/** Send data
 * @param buf           the data to be sent
 * @param len           length of the data in bytes
 * @return        zero on success, nonzero on failure
 */
int
serial_write (const void* buf, unsigned len)
{
# ifdef WIN32
  unsigned long length;
  if (!WriteFile (serialfd, buf, len, &length, 0)) {
    report_error ("serial_write: WriteFile", 0);
    return 1;
  }
  else if (length != len) {
    fprintf (stderr, "serial_write: WriteFile: "
           "wrote %lu of %u bytes\n", length, len);
    return 1;
  }
# elif defined __AMIGA__
  ser.IOSer.io_Command = CMD_WRITE;
  ser.IOSer.io_Length = len;
  ser.IOSer.io_Data = (void*) buf;
  SendIO ((struct IORequest*) &ser);
  if (flush_serial ()) {
    fprintf (stderr, "serial_write: WaitIO (CMD_WRITE) returned %d\n",
           ser.IOSer.io_Error);
    return 1;
  }
# else /* !WIN32 && !AMIGA */
  if (len != write (serialfd, buf, len)) {
    perror ("serial_write: write");
    return 1;
  }
# endif /* !WIN32 && !AMIGA */
  return 0;
}

/** Receive data with or without calibration
 * @param buf           the data to be received
 * @param len           length of the data in bytes
 * @return        zero on success, nonzero on failure
 */
int
serial_read (void* buf, unsigned len)
{
# ifdef __AMIGA__
  ser.IOSer.io_Command = CMD_READ;
  ser.IOSer.io_Length = len;
  ser.IOSer.io_Data = buf;
  SendIO ((struct IORequest*) &ser);
  if (flush_serial ()) {
    fprintf (stderr, "serial_read: WaitIO (CMD_READ) returned %d\n",
           ser.IOSer.io_Error);
    return 1;
  }
# else /* !AMIGA */
  for (;;) {
#  ifdef WIN32
    unsigned long got;
    if (!ReadFile (serialfd, buf, len, &got, 0)) {
      report_error ("serial_read: ReadFile", 0);
      return 1;
    }
    else if (!got) {
      fputs ("serial_read: truncated data\n", stderr);
      return 1;
    }
#  else /* !WIN32 */
    unsigned got = read (serialfd, buf, len);
    if (!got) {
      if (errno)
      perror ("serial_read: read");
      else
      fputs ("serial_read: truncated data\n", stderr);
      return 1;
    }
#  endif /* !WIN32 */
    if (got > len) {
      fputs ("serial_read: too much data\n", stderr);
      return 5;
    }
    buf = (char*) buf + got;
    if (!(len -= got))
      break;
  }
# endif /* !AMIGA */

  return 0;
}
#endif /* COMM_SERIAL */

Generated by  Doxygen 1.6.0   Back to index