Add glibc support(default)

Change-Id: I7675edcf14df8707ecd424a962e4cc464a4c6ae4
diff --git a/mbtk/aboot-tiny/jacana/jacana_serialport_posix.c b/mbtk/aboot-tiny/jacana/jacana_serialport_posix.c
new file mode 100755
index 0000000..c5c10bb
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_serialport_posix.c
@@ -0,0 +1,526 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <termios.h>
+#include <assert.h>
+#include "aboot-tiny.h"
+#include "jacana_serialport.h"
+#include <time.h>
+#include "jacana_usleep.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef SERIAL_RX_BUF_SIZE
+#define SERIAL_RX_BUF_SIZE (1024)
+#endif
+
+typedef enum {
+  SERIALPORT_PARITY_NONE  = 1,
+  SERIALPORT_PARITY_MARK  = 2,
+  SERIALPORT_PARITY_EVEN  = 3,
+  SERIALPORT_PARITY_ODD   = 4,
+  SERIALPORT_PARITY_SPACE = 5
+} serialport_parity_t;
+
+typedef enum {
+  SERIALPORT_STOPBITS_ONE         = 1,
+  SERIALPORT_STOPBITS_ONE_FIVE    = 2,
+  SERIALPORT_STOPBITS_TWO         = 3
+} serialport_stop_bits_t;
+
+typedef struct {
+  int baud;
+  int data_bits;
+  bool rtscts;
+  bool xon;
+  bool xoff;
+  bool xany;
+  bool dsrdtr;
+  bool hupcl;
+  serialport_parity_t parity;
+  serialport_stop_bits_t stop_bits;
+} serial_port_connect_opt_t;
+/*---------------------------------------------------------------------------*/
+typedef struct serial_tx_memb {
+  struct serial_tx_memb *next;
+  uint8_t *data;
+  int len;
+  int remain;
+} serial_tx_memb_t;
+/*---------------------------------------------------------------------------*/
+static uint8_t rx_buf[SERIAL_RX_BUF_SIZE];
+static aboot_tiny_uart_rx_callback_t rx_cb;
+static int serial_fd = -1;
+static int fd_error;
+/*---------------------------------------------------------------------------*/
+static serial_tx_memb_t *tx_queue_list;
+static serial_tx_memb_t **tx_queue = &tx_queue_list;
+/*---------------------------------------------------------------------------*/
+static inline void
+queue_init(serial_tx_memb_t **queue)
+{
+  *queue = NULL;
+}
+/*---------------------------------------------------------------------------*/
+static inline bool
+queue_is_empty(serial_tx_memb_t **queue)
+{
+  return *queue == NULL ? true : false;
+}
+/*---------------------------------------------------------------------------*/
+static inline serial_tx_memb_t *
+queue_peek(serial_tx_memb_t **queue)
+{
+  return *queue;
+}
+/*---------------------------------------------------------------------------*/
+static inline serial_tx_memb_t *
+queue_dequeue(serial_tx_memb_t **queue)
+{
+  serial_tx_memb_t *l;
+  l = *queue;
+  if(*queue != NULL) {
+    *queue = ((serial_tx_memb_t *)*queue)->next;
+  }
+
+  return l;
+}
+/*---------------------------------------------------------------------------*/
+static inline void
+queue_enqueue(serial_tx_memb_t **queue, serial_tx_memb_t *element)
+{
+  element->next = NULL;
+
+  if(*queue == NULL) {
+    *queue = element;
+  } else {
+    serial_tx_memb_t *l;
+    for(l = *queue; l->next != NULL; l = l->next) {
+    }
+    l->next = element;
+  }
+}
+/*---------------------------------------------------------------------------*/
+static int
+set_fd(fd_set *rset, fd_set *wset)
+{
+  if(serial_fd > 0 && !fd_error) {
+    FD_SET(serial_fd, rset);
+    if(!queue_is_empty(tx_queue)) {
+      FD_SET(serial_fd, wset);
+    }
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/*---------------------------------------------------------------------------*/
+static void
+serial_do_tx(void)
+{
+  serial_tx_memb_t *p;
+  int cnt, offset;
+
+  p = queue_peek(tx_queue);
+  if(p) {
+    offset = p->len - p->remain;
+    cnt = write(serial_fd, p->data + offset, p->remain);
+
+#if 0
+    unsigned int ii,j;
+    ii = p->remain;
+    if(ii > 128)
+    {
+        ii = 128;
+    }
+
+    char buff[512];
+    memset(buff, 0, sizeof(buff));
+    for(j=0;j<ii;j++)
+    {
+        sprintf(&buff[j], "%c", *(p->data + offset+j));
+    }
+
+    aboot_tiny_log_printf("Tlen=%d,data=%s", p->remain, buff);
+#endif
+
+    if(cnt > 0) {
+      p->remain -= cnt;
+      if(p->remain == 0) {
+        queue_dequeue(tx_queue);
+        aboot_tiny_mem_free(p->data);
+        aboot_tiny_mem_free(p);
+      }
+    } else if(cnt < 0) {
+      aboot_tiny_log_printf("write error: error code %d\n", errno);
+      fd_error = errno;
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+static void
+handle_fd(fd_set *rset, fd_set *wset)
+{
+  int cnt;
+  if(serial_fd >= 0 && !fd_error) {
+    if(FD_ISSET(serial_fd, wset)) {
+      serial_do_tx();
+      if(queue_is_empty(tx_queue)) {
+        FD_CLR(serial_fd, wset);
+      }
+    }
+
+    if(FD_ISSET(serial_fd, rset)) {
+      cnt = read(serial_fd, rx_buf, sizeof(rx_buf));
+#if 0
+      unsigned int ii,j;
+      ii = cnt;
+      if(ii > 64)
+      {
+          ii = 64;
+      }
+      
+      char buf[256];
+      memset(buf, 0, sizeof(buf));
+      for(j=0;j<ii;j++)
+      {
+          sprintf(&buf[j], "%c,", rx_buf[j]);
+      }
+      
+      aboot_tiny_log_printf("Rlen=%d,data=%s", cnt, buf);
+#endif
+      if((cnt > 0) && rx_cb) {
+        rx_cb(rx_buf, cnt);
+      } else if(cnt < 0) {
+        aboot_tiny_log_printf("read error: error code %d\n", errno);
+        fd_error = errno;
+      }
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+static const struct select_callback serial_select_callback = {
+  set_fd,
+  handle_fd,
+};
+/*---------------------------------------------------------------------------*/
+static int
+to_baud_const(int baud)
+{
+  switch(baud) {
+  case 0: return B0;
+  case 50: return B50;
+  case 75: return B75;
+  case 110: return B110;
+  case 134: return B134;
+  case 150: return B150;
+  case 200: return B200;
+  case 300: return B300;
+  case 600: return B600;
+  case 1200: return B1200;
+  case 1800: return B1800;
+  case 2400: return B2400;
+  case 4800: return B4800;
+  case 9600: return B9600;
+  case 19200: return B19200;
+  case 38400: return B38400;
+  case 57600: return B57600;
+  case 115200: return B115200;
+  case 230400: return B230400;
+#if defined(__linux__)
+  case 460800: return B460800;
+  case 500000: return B500000;
+  case 576000: return B576000;
+  case 921600: return B921600;
+  case 1000000: return B1000000;
+  case 1152000: return B1152000;
+  case 1500000: return B1500000;
+  case 2000000: return B2000000;
+  case 2500000: return B2500000;
+  case 3000000: return B3000000;
+  case 3500000: return B3500000;
+  case 4000000: return B4000000;
+#endif
+  }
+  return -1;
+}
+/*---------------------------------------------------------------------------*/
+static int
+to_data_bits_const(int data_bits)
+{
+  switch(data_bits) {
+  case 8: default: return CS8;
+  case 7: return CS7;
+  case 6: return CS6;
+  case 5: return CS5;
+  }
+  return -1;
+}
+/*---------------------------------------------------------------------------*/
+static int
+set_baudrate(int fd, serial_port_connect_opt_t *opt)
+{
+  /* lookup the standard baudrates from the table */
+  int baud_rate = to_baud_const(opt->baud);
+
+  /* get port options */
+  struct termios options;
+
+  if(-1 == tcgetattr(fd, &options)) {
+    aboot_tiny_log_printf("Error: %s setting custom baud rate of %d", strerror(errno), opt->baud);
+    return -1;
+  }
+
+  if(-1 == baud_rate) {
+    aboot_tiny_log_printf("Error baud rate of %d is not supported on your platform", opt->baud);
+    return -1;
+  }
+
+  /* If we have a good baud rate set it and lets go */
+  cfsetospeed(&options, baud_rate);
+  cfsetispeed(&options, baud_rate);
+  /* throw away all the buffered data */
+  tcflush(fd, TCIOFLUSH);
+  /* make the changes now */
+  tcsetattr(fd, TCSANOW, &options);
+
+  return 1;
+}
+
+/*---------------------------------------------------------------------------*/
+static int
+set_default_baudrate(int fd)
+{
+  /* Default BaudRate => 115200. Just for AP UART. */
+  int baud_rate = to_baud_const(115200);
+
+  /* get port options */
+  struct termios options;
+
+  if(-1 == tcgetattr(fd, &options))
+    return -1;
+
+
+  if(-1 == baud_rate)
+    return -1;
+
+  /* If we have a good baud rate set it and lets go */
+  cfsetospeed(&options, baud_rate);
+  cfsetispeed(&options, baud_rate);
+  /* throw away all the buffered data */
+  tcflush(fd, TCIOFLUSH);
+  /* make the changes now */
+  tcsetattr(fd, TCSANOW, &options);
+
+  return 1;
+}
+
+
+/*---------------------------------------------------------------------------*/
+static int
+setup(int fd, serial_port_connect_opt_t *opt)
+{
+  int data_bits = to_data_bits_const(opt->data_bits);
+  if(data_bits == -1) {
+    aboot_tiny_log_printf("Invalid data bits %d\n", opt->data_bits);
+    return -1;
+  }
+  if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+    aboot_tiny_log_printf("Set FD_CLOEXEC failed\n");
+    return -1;
+  }
+
+  /* Get port configuration for modification */
+  struct termios options;
+  tcgetattr(fd, &options);
+
+  /* IGNPAR: ignore bytes with parity errors */
+  options.c_iflag = IGNPAR;                
+  /* ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input) */
+  /* Future potential option */
+  /* options.c_iflag = ICRNL; */
+  /* otherwise make device raw (no other input processing) */
+
+  /* Specify data bits */
+  options.c_cflag &= ~CSIZE;
+  options.c_cflag |= data_bits;
+
+  options.c_cflag &= ~(CRTSCTS);
+
+  if(opt->rtscts) {
+    options.c_cflag |= CRTSCTS;
+  }
+
+  options.c_iflag &= ~(IXON | IXOFF | IXANY);
+
+  if(opt->xon) {
+    options.c_iflag |= IXON;
+  }
+
+  if(opt->xoff) {
+    options.c_iflag |= IXOFF;
+  }
+
+  if(opt->xany) {
+    options.c_iflag |= IXANY;
+  }
+
+  switch(opt->parity) {
+  case SERIALPORT_PARITY_NONE:
+    options.c_cflag &= ~PARENB;
+    break;
+  case SERIALPORT_PARITY_ODD:
+    options.c_cflag |= PARENB;
+    options.c_cflag |= PARODD;
+    break;
+  case SERIALPORT_PARITY_EVEN:
+    options.c_cflag |= PARENB;
+    options.c_cflag &= ~PARODD;
+    break;
+  default:
+    aboot_tiny_log_printf("Invalid parity setting %d", opt->parity);
+    return -1;
+  }
+
+  switch(opt->stop_bits) {
+  case SERIALPORT_STOPBITS_ONE:
+    options.c_cflag &= ~CSTOPB;
+    break;
+  case SERIALPORT_STOPBITS_TWO:
+    options.c_cflag |= CSTOPB;
+    break;
+  default:
+    aboot_tiny_log_printf("Invalid stop bits setting %d", opt->stop_bits);
+    return -1;
+  }
+
+
+  options.c_cflag |= CLOCAL;      /* ignore status lines */
+  options.c_cflag |= CREAD;       /* enable receiver */
+  options.c_oflag = 0;
+  /* ICANON makes partial lines not readable. It should be optional. */
+  /* It works with ICRNL. */
+  options.c_lflag = 0; /* ICANON; */
+  options.c_cc[VMIN] = 0;
+  options.c_cc[VTIME] = 0;
+
+  tcsetattr(fd, TCSANOW, &options);
+
+
+  if(set_baudrate(fd, opt) == -1) {
+    return -1;
+  }
+
+  return 1;
+}
+/*---------------------------------------------------------------------------*/
+static int
+serial_port_open(const char *dev, serial_port_connect_opt_t *opt)
+{
+  assert(serial_fd == -1);   /* previously port has not been closed */
+  serial_fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC);
+  if(serial_fd < 0) {
+    return -1;
+  }
+  if(setup(serial_fd, opt) < 0) {
+    return -1;
+  }
+  rx_cb = NULL;
+  fd_error = 0;
+  aboot_tiny_log_printf("serial fd=%d rate=%d", serial_fd, opt->baud);
+  aboot_tiny_select_set_callback(serial_fd, &serial_select_callback);
+  queue_init(tx_queue);
+
+  return 0;
+}
+static void
+serial_port_close(void)
+{
+  serial_tx_memb_t *p;
+
+  if(serial_fd >= 0) {
+    aboot_tiny_select_set_callback(serial_fd, NULL);
+    rx_cb = NULL;
+    /* BaudRate = 115200bps. */
+    set_default_baudrate(serial_fd);
+    close(serial_fd);
+    serial_fd = -1;
+    while(1) {
+      p = queue_dequeue(tx_queue);
+      if(!p) {
+        break;
+      }
+      aboot_tiny_mem_free(p->data);
+      aboot_tiny_mem_free(p);
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_serialport_write(const uint8_t *buf, size_t len)
+{
+  serial_tx_memb_t *p;
+
+  if(serial_fd < 0 || fd_error) {
+    return len;
+  }
+
+  p = aboot_tiny_mem_alloc(sizeof(serial_tx_memb_t));
+  if(!p) {
+    aboot_tiny_log_printf("out of memory\n");
+    return -1;
+  }
+
+  p->data = aboot_tiny_mem_alloc(len);
+  if(!p->data) {
+    aboot_tiny_mem_free((void *)p);
+    aboot_tiny_log_printf("out of memory\n");
+    return -1;
+  }
+  memcpy(p->data, (void *)buf, len);
+  p->len = p->remain = len;
+  queue_enqueue(tx_queue, p);
+
+  return len;
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_serialport_init(const char *dev, int baud,
+                       aboot_tiny_uart_rx_callback_t cb)
+{
+  serial_port_connect_opt_t connect_op;
+  connect_op.baud = baud;
+  connect_op.data_bits = 8;
+  connect_op.rtscts = false;
+  connect_op.xon = false;
+  connect_op.xoff = false;
+  connect_op.xany = false;
+  connect_op.dsrdtr = false;
+  connect_op.hupcl = false;
+  connect_op.parity = SERIALPORT_PARITY_NONE;
+  connect_op.stop_bits = SERIALPORT_STOPBITS_ONE;
+
+  if(!serial_port_open(dev, &connect_op)) {
+    rx_cb = cb;
+    return 0;
+  } else {
+    aboot_tiny_log_printf("open device \"%s\" failed\n", dev);
+    return -1;
+  }
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_serialport_exit(void)
+{
+  serial_port_close();
+}
+/*---------------------------------------------------------------------------*/