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

#include "serial_device.h"
#include "connect_device.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <termios.h>
#include <sys/poll.h>
#include <unistd.h>

typedef struct {
  int fd;
  struct termios sio;
  struct pollfd nfds;
} ids_info_t;
static ids_info_t ids[ID_MAX];
static int last_id;


int serial_open(const char *devName, long baudrate) {
  int fd;
  ids[last_id].fd = -1;

  fd = open(devName, O_RDWR | O_SYNC);
  if (fd < 0) {
    perror(devName);
    return DEVICE_OPEN_ERROR;
  }

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

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

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

  return last_id++;
}


void serial_close(int id) {
  close(ids[id].fd);
  ids[id].fd = -1;
}


int serial_is_connected(int id) {
  return ids[id].fd;
}


int serial_recv(int id, char *data, int size, int timeout) {
  struct pollfd *nfds = &ids[id].nfds;
  int fd = ids[id].fd;
  int filled = 0;
  int n;

  while (filled < size) {
    if (poll(nfds, 1, timeout) == 0) {
      break;                    // timeout
    }
    n = read(fd, &data[filled], size - filled);
    if (n <= 0) {
      break;
    }
    filled += n;
  }
  return filled;
}


int serial_send(int id, const char *data, int length) {
  return write(ids[id].fd, data, length);
}


void serial_flush(int id) {
  tcflush(ids[id].fd, TCIOFLUSH);
}


int serial_set_baudrate(int id, long baudrate) {
  struct termios *sio = &ids[id].sio;
  int fd = ids[id].fd;
  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 230400:
    baudrate_value = B230400;
    break;

  default:
    return BAUDRATE_ADJUST_ERROR;
    break;
  }

  cfsetospeed(sio, baudrate_value);
  cfsetispeed(sio, baudrate_value);
  tcsetattr(fd, TCSAFLUSH, sio);

  return 0;
}
