/*
  シリアル接続制御
  Satofumi KAMIMURA
  $Id: serialDevice_lin.cpp 286 2008-10-20 09:40:22Z satofumi $
*/

#include "serialDevice.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>


SerialDevice::~SerialDevice(void) {
  raw_disconnect();
}


const char* SerialDevice::what(void) {
  return error_message.c_str();
}


SerialDevice::SerialDevice(int buffer_size)
  : ConnectionDevice(buffer_size), fd(-1),
    error_message("Connection device is not specified") {
}


int SerialDevice::raw_connect(const char* device, long baudrate) {

  fd = open(device, O_RDWR | O_SYNC);
  if (fd < 0) {
    perror(device);
    error_message = std::string("Couldn't open device: ") + device;
    return DeviceOpenFail;
  }

  tcgetattr(fd, &sio);
  sio.c_iflag = IGNPAR;
  sio.c_oflag = 0;
  sio.c_cflag = CS8 | CREAD | CLOCAL;
  sio.c_lflag = 0;

  sio.c_cc[VMIN] = 0;
  sio.c_cc[VTIME] = 0;
  tcsetattr(fd, TCSAFLUSH, &sio);

  /* タイムアウト設定 */
  nfds.fd = fd;
  nfds.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
  nfds.revents = 0;

  error_message = "Success";
  return 0;
}


void SerialDevice::raw_disconnect(void) {
  close(fd);
  fd = -1;
}


bool SerialDevice::isConnected(void) {
  return (fd < 0) ? false : true;
}


int SerialDevice::raw_setBaudrate(long baudrate) {
  long baudrate_value;
  switch (baudrate) {

  case 9600:
    baudrate_value = B9600;
    break;

  case 19200:
    baudrate_value = B19200;
    break;

  case 38400:
    baudrate_value = B38400;
    break;

  case 57600:
    baudrate_value = B57600;
    break;

  case 115200:
    baudrate_value = B115200;
    break;

  case B230400:
    baudrate_value = B230400;
    break;

  default:
    char* message = new char [80 + 12];
    sprintf(message, "Invalid baudrate value: %ld", baudrate);
    error_message = message;
    delete [] message;
    return InvalidBaudrate;
    break;
  }

  cfsetospeed(&sio, baudrate_value);
  cfsetispeed(&sio, baudrate_value);
  tcsetattr(fd, TCSADRAIN, &sio);

  error_message = "Success";
  return 0;
}


int SerialDevice::raw_send(const char* data, int len) {
#if 0
  for (int i = 0; i < len; ++i) {
    if (data[i] == '\r') {
      fprintf(stderr, "\n");
    } else {
      fprintf(stderr, "%c", data[i]);
    }
  }
#endif
  return write(fd, data, len);
}


void SerialDevice::raw_flush(void) {
#if 0
  tcflush(fd, TCIOFLUSH);
  raw_check(BufferSize, 0);
  recv_buffer->clear();
#endif

  tcflush(fd, TCIOFLUSH);
  do {
    recv_buffer->clear();
    raw_check(BufferSize, 10);
  } while (recv_buffer->size() > 0);
}


void SerialDevice::raw_check(int size, long timeout) {

  int filled = 0;
  while (filled < size) {
    if (poll(&nfds, 1, timeout) <= 0) {
      break;                    // timeout
    }
    char buffer[BufferSize];
    int n = read(fd, buffer, size);

#if 0
  for (int i = 0; i < n; ++i) {
    if (buffer[i] == '\r') {
      fprintf(stderr, "\n");
    } else {
      fprintf(stderr, "%c", buffer[i]);
    }
  }
#endif

    if (n <= 0) {
      return;
    }
    recv_buffer->put(buffer, n);
    filled += n;
  }
}
