uart.c: make use of no_bus_mux flag
[contrib/mraa.git] / src / uart / uart.c
index 389ba2c..499af93 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * Author: Thomas Ingleby <thomas.c.ingleby@intel.com>
- * Copyright (c) 2014 Intel Corporation.
+ * Contributions: Jon Trulson <jtrulson@ics.com>
+ *                Brendan le Foll <brendan.le.foll@intel.com>
+ * Copyright (c) 2014 - 2015 Intel Corporation.
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
 #include <sys/stat.h>
 #include <unistd.h>
 #include <string.h>
+#include <termios.h>
 
 #include "uart.h"
 #include "mraa_internal.h"
 
+// This function takes an unsigned int and converts it to a B* speed_t
+// that can be used with linux/posix termios
+static speed_t
+uint2speed(unsigned int speed)
+{
+    switch (speed) {
+        case 0:
+            return B0; // hangup, not too useful otherwise
+        case 50:
+            return B50;
+        case 75:
+            return B75;
+        case 110:
+            return B110;
+        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;
+        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;
+        default:
+            // if we are here, then an unsupported baudrate was selected.
+            // Report it via syslog and return B9600, a common default.
+            syslog(LOG_ERR, "uart: unsupported baud rate, defaulting to 9600.");
+            return B9600;
+    }
+}
+
+static mraa_uart_context
+mraa_uart_init_internal(mraa_adv_func_t* func_table)
+{
+    mraa_uart_context dev = (mraa_uart_context) calloc(1, sizeof(struct _uart));
+    if (dev == NULL) {
+        syslog(LOG_CRIT, "uart: Failed to allocate memory for context");
+        return NULL;
+    }
+    dev->index = -1;
+    dev->fd = -1;
+    dev->advance_func = func_table;
+
+    return dev;
+}
+
 mraa_uart_context
 mraa_uart_init(int index)
 {
-    if ( mraa_setup_uart(index) != MRAA_SUCCESS)
+    if (plat == NULL) {
+        syslog(LOG_ERR, "uart: platform not initialised");
+        return NULL;
+    }
+
+    if (mraa_is_sub_platform_id(index)) {
+        syslog(LOG_NOTICE, "uart: Using sub platform is not supported");
+        return NULL;
+    }
+
+    if (plat->adv_func->uart_init_pre != NULL) {
+        if (plat->adv_func->uart_init_pre(index) != MRAA_SUCCESS) {
+            syslog(LOG_ERR, "uart: failure in pre-init platform hook");
+            return NULL;
+        }
+    }
+
+    if (plat->uart_dev_count == 0) {
+        syslog(LOG_ERR, "uart: platform has no UARTs defined");
+        return NULL;
+    }
+
+    if (plat->uart_dev_count <= index) {
+        syslog(LOG_ERR, "uart: platform has only %i", plat->uart_dev_count);
         return NULL;
+    }
+
+    if (!plat->no_bus_mux) {
+        int pos = plat->uart_dev[index].rx;
+        if (pos >= 0) {
+            if (plat->pins[pos].uart.mux_total > 0) {
+                if (mraa_setup_mux_mapped(plat->pins[pos].uart) != MRAA_SUCCESS) {
+                    syslog(LOG_ERR, "uart: failed to setup muxes for RX pin");
+                    return NULL;
+                }
+            }
+        }
 
-    mraa_uart_context dev = (mraa_uart_context) malloc(sizeof(struct _uart));
-    memset(dev, 0, sizeof(struct _uart));
+        pos = plat->uart_dev[index].tx;
+        if (pos >= 0) {
+            if (plat->pins[pos].uart.mux_total > 0) {
+                if (mraa_setup_mux_mapped(plat->pins[pos].uart) != MRAA_SUCCESS) {
+                    syslog(LOG_ERR, "uart: failed to setup muxes for TX pin");
+                    return NULL;
+                }
+            }
+        }
+    }
 
-    dev->index = index;
-    if (advance_func->uart_init_post != NULL) {
-        mraa_result_t ret = advance_func->uart_init_post(dev);
+    mraa_uart_context dev = mraa_uart_init_raw((char*)plat->uart_dev[index].device_path);
+    if (dev == NULL) {
+        return NULL;
+    }
+    dev->index = index; //Set the board Index.
+
+    if (IS_FUNC_DEFINED(dev, uart_init_post)) {
+        mraa_result_t ret = dev->advance_func->uart_init_post(dev);
         if (ret != MRAA_SUCCESS) {
             free(dev);
             return NULL;
@@ -50,3 +190,325 @@ mraa_uart_init(int index)
 
     return dev;
 }
+
+mraa_uart_context
+mraa_uart_init_raw(const char* path)
+{
+    mraa_uart_context dev = mraa_uart_init_internal(plat == NULL ? NULL : plat->adv_func);
+    if (dev == NULL) {
+        syslog(LOG_ERR, "uart: Failed to allocate memory for context");
+        return NULL;
+    }
+    dev->path = path;
+
+    if (!dev->path) {
+        syslog(LOG_ERR, "uart: device path undefined, open failed");
+        free(dev);
+        return NULL;
+    }
+
+    // now open the device
+    if ((dev->fd = open(dev->path, O_RDWR)) == -1) {
+        syslog(LOG_ERR, "uart: open() failed");
+        free(dev);
+        return NULL;
+    }
+
+    // now setup the tty and the selected baud rate
+    struct termios termio;
+
+    // get current modes
+    if (tcgetattr(dev->fd, &termio)) {
+        syslog(LOG_ERR, "uart: tcgetattr() failed");
+        close(dev->fd);
+        free(dev);
+        return NULL;
+    }
+
+    // setup for a 'raw' mode.  8N1, no echo or special character
+    // handling, such as flow control or line editing semantics.
+    // cfmakeraw is not POSIX!
+    cfmakeraw(&termio);
+    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
+        syslog(LOG_ERR, "uart: tcsetattr() failed after cfmakeraw()");
+        close(dev->fd);
+        free(dev);
+        return NULL;
+    }
+
+    if (mraa_uart_set_baudrate(dev, 9600) != MRAA_SUCCESS) {
+        close(dev->fd);
+        free(dev);
+        return NULL;
+    }
+
+    return dev;
+}
+
+mraa_result_t
+mraa_uart_stop(mraa_uart_context dev)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: stop: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    // just close the device and reset our fd.
+    if (dev->fd >= 0) {
+        close(dev->fd);
+    }
+
+    free(dev);
+
+    return MRAA_SUCCESS;
+}
+
+mraa_result_t
+mraa_uart_flush(mraa_uart_context dev)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: stop: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    if (tcdrain(dev->fd) == -1) {
+        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
+    }
+
+    return MRAA_SUCCESS;
+}
+
+mraa_result_t
+mraa_uart_set_baudrate(mraa_uart_context dev, unsigned int baud)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: stop: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    struct termios termio;
+    if (tcgetattr(dev->fd, &termio)) {
+        syslog(LOG_ERR, "uart: tcgetattr() failed");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    // set our baud rates
+    speed_t speed = uint2speed(baud);
+    cfsetispeed(&termio, speed);
+    cfsetospeed(&termio, speed);
+
+    // make it so
+    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
+        syslog(LOG_ERR, "uart: tcsetattr() failed");
+        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
+    }
+    return MRAA_SUCCESS;
+}
+
+mraa_result_t
+mraa_uart_set_mode(mraa_uart_context dev, int bytesize, mraa_uart_parity_t parity, int stopbits)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: stop: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    struct termios termio;
+    if (tcgetattr(dev->fd, &termio)) {
+        syslog(LOG_ERR, "uart: tcgetattr() failed");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    termio.c_cflag &= ~CSIZE;
+    switch (bytesize) {
+        case 8:
+            termio.c_cflag |= CS8;
+            break;
+        case 7:
+            termio.c_cflag |= CS7;
+            break;
+        case 6:
+            termio.c_cflag |= CS6;
+            break;
+        case 5:
+            termio.c_cflag |= CS5;
+            break;
+        default:
+            termio.c_cflag |= CS8;
+            break;
+    }
+
+    // POSIX & linux doesn't support 1.5 and I've got bigger fish to fry
+    switch (stopbits) {
+        case 1:
+            termio.c_cflag &= ~CSTOPB;
+            break;
+        case 2:
+            termio.c_cflag |= CSTOPB;
+        default:
+            break;
+    }
+
+    switch (parity) {
+        case MRAA_UART_PARITY_NONE:
+            termio.c_cflag &= ~(PARENB | PARODD);
+            break;
+        case MRAA_UART_PARITY_EVEN:
+            termio.c_cflag |= PARENB;
+            termio.c_cflag &= ~PARODD;
+            break;
+        case MRAA_UART_PARITY_ODD:
+            termio.c_cflag |= PARENB | PARODD;
+            break;
+        case MRAA_UART_PARITY_MARK: // not POSIX
+            termio.c_cflag |= PARENB | CMSPAR | PARODD;
+            break;
+        case MRAA_UART_PARITY_SPACE: // not POSIX
+            termio.c_cflag |= PARENB | CMSPAR;
+            termio.c_cflag &= ~PARODD;
+            break;
+    }
+
+    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
+        syslog(LOG_ERR, "uart: tcsetattr() failed");
+        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
+    }
+
+    return MRAA_SUCCESS;
+}
+
+mraa_result_t
+mraa_uart_set_flowcontrol(mraa_uart_context dev, mraa_boolean_t xonxoff, mraa_boolean_t rtscts)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: stop: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    // hardware flow control
+    int action = TCIOFF;
+    if (xonxoff) {
+        action = TCION;
+    }
+    if (tcflow(dev->fd, action)) {
+        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
+    }
+
+    // rtscts
+    struct termios termio;
+
+    // get current modes
+    if (tcgetattr(dev->fd, &termio)) {
+        syslog(LOG_ERR, "uart: tcgetattr() failed");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    if (rtscts) {
+        termio.c_cflag |= CRTSCTS;
+    } else {
+        termio.c_cflag &= ~CRTSCTS;
+    }
+
+    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
+        syslog(LOG_ERR, "uart: tcsetattr() failed");
+        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
+    }
+
+    return MRAA_SUCCESS;
+}
+
+mraa_result_t
+mraa_uart_set_timeout(mraa_uart_context dev, int read, int write, int interchar)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: stop: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+const char*
+mraa_uart_get_dev_path(mraa_uart_context dev)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: get_device_path failed, context is NULL");
+        return NULL;
+    }
+    if (dev->path == NULL) {
+        syslog(LOG_ERR, "uart: device path undefined");
+        return NULL;
+    }
+
+    return dev->path;
+}
+
+int
+mraa_uart_read(mraa_uart_context dev, char* buf, size_t len)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: read: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    if (dev->fd < 0) {
+        syslog(LOG_ERR, "uart: port is not open");
+        return MRAA_ERROR_INVALID_RESOURCE;
+    }
+
+    return read(dev->fd, buf, len);
+}
+
+int
+mraa_uart_write(mraa_uart_context dev, const char* buf, size_t len)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: write: context is NULL");
+        return MRAA_ERROR_INVALID_HANDLE;
+    }
+
+    if (dev->fd < 0) {
+        syslog(LOG_ERR, "uart: port is not open");
+        return MRAA_ERROR_INVALID_RESOURCE;
+    }
+
+    return write(dev->fd, buf, len);
+}
+
+mraa_boolean_t
+mraa_uart_data_available(mraa_uart_context dev, unsigned int millis)
+{
+    if (!dev) {
+        syslog(LOG_ERR, "uart: data_available: write context is NULL");
+        return 0;
+    }
+
+    if (dev->fd < 0) {
+        syslog(LOG_ERR, "uart: port is not open");
+        return 0;
+    }
+
+    struct timeval timeout;
+
+    if (millis == 0) {
+        // no waiting
+        timeout.tv_sec = 0;
+        timeout.tv_usec = 0;
+    } else {
+        timeout.tv_sec = millis / 1000;
+        timeout.tv_usec = (millis % 1000) * 1000;
+    }
+
+    fd_set readfds;
+
+    FD_ZERO(&readfds);
+    FD_SET(dev->fd, &readfds);
+
+    if (select(dev->fd + 1, &readfds, NULL, NULL, &timeout) > 0) {
+        return 1; // data is ready
+    } else {
+        return 0;
+    }
+}
+
+