diff --git a/api/mraa/types.h b/api/mraa/types.h index 52fa312..097ee99 100644 --- a/api/mraa/types.h +++ b/api/mraa/types.h @@ -222,6 +222,14 @@ typedef enum { MRAA_I2C_HIGH = 2 /**< up to 3.4Mhz */ } mraa_i2c_mode_t; +typedef enum { + MRAA_UART_PARITY_NONE = 0, + MRAA_UART_PARITY_EVEN = 1, + MRAA_UART_PARITY_ODD = 2, + MRAA_UART_PARITY_MARK = 3, + MRAA_UART_PARITY_SPACE = 4 +} mraa_uart_parity_t; + #ifdef __cplusplus } #endif diff --git a/api/mraa/uart.h b/api/mraa/uart.h index 298329c..b1d5be8 100644 --- a/api/mraa/uart.h +++ b/api/mraa/uart.h @@ -1,7 +1,8 @@ /* * Author: Thomas Ingleby * Contributions: Jon Trulson - * Copyright (c) 2014 Intel Corporation. + * Brendan Le Foll + * 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 @@ -64,6 +65,18 @@ typedef struct _uart* mraa_uart_context; */ mraa_uart_context mraa_uart_init(int uart); +mraa_uart_context mraa_uart_init_raw(int uart); + +mraa_result_t mraa_uart_flush(mraa_uart_context); + +mraa_result_t mraa_uart_set_baudrate(mraa_uart_context dev, unsigned int baud); + +mraa_result_t mraa_uart_set_mode(mraa_uart_context dev, int bytesize, mraa_uart_parity_t parity, int stopbits); + +mraa_result_t mraa_uart_set_flowcontrol(mraa_uart_context dev, mraa_boolean_t xonxoff, mraa_boolean_t rtscts); + +mraa_result_t mraa_uart_set_timeout(mraa_uart_context dev, int read, int write, int interchar); + /** * Get Char pointer with tty device path within Linux * For example. Could point to "/dev/ttyS0" @@ -86,12 +99,12 @@ char* mraa_uart_get_dev_path(mraa_uart_context dev); mraa_result_t mraa_uart_open_dev(mraa_uart_context dev, unsigned int baud); /** - * Close a device previously opened with mraa_uart_open_dev(). + * Destroy a mraa_uart_context * * @param dev uart context * @return mraa_result_t */ -mraa_result_t mraa_uart_close_dev(mraa_uart_context dev); +mraa_result_t mraa_uart_stop(mraa_uart_context dev); /** * Read bytes from the device into a buffer @@ -101,7 +114,7 @@ mraa_result_t mraa_uart_close_dev(mraa_uart_context dev); * @param len maximum size of buffer * @return the number of bytes read, or -1 if an error occurred */ -int mraa_uart_read(mraa_uart_context dev, char *buf, size_t len); +int mraa_uart_read(mraa_uart_context dev, char* buf, size_t len); /** * Write bytes in buffer to a device @@ -111,7 +124,7 @@ int mraa_uart_read(mraa_uart_context dev, char *buf, size_t len); * @param len maximum size of buffer * @return the number of bytes written, or -1 if an error occurred */ -int mraa_uart_write(mraa_uart_context dev, char *buf, size_t len); +int mraa_uart_write(mraa_uart_context dev, char* buf, size_t len); /** * Check to see if data is available on the device for reading @@ -120,8 +133,7 @@ int mraa_uart_write(mraa_uart_context dev, char *buf, size_t len); * @param millis number of milliseconds to wait, or 0 to return immediately * @return 1 if there is data available to read, 0 otherwise */ -mraa_boolean_t -mraa_uart_data_available(mraa_uart_context dev, unsigned int millis); +mraa_boolean_t mraa_uart_data_available(mraa_uart_context dev, unsigned int millis); #ifdef __cplusplus } diff --git a/api/mraa/uart.hpp b/api/mraa/uart.hpp index 5642190..928e149 100644 --- a/api/mraa/uart.hpp +++ b/api/mraa/uart.hpp @@ -1,7 +1,7 @@ /* * Author: Brendan Le Foll * Contributions: Jon Trulson - * Copyright (c) 2014 Intel Corporation. + * 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 @@ -110,7 +110,7 @@ class Uart * @return the number of bytes read, or -1 if an error occurred */ int - read(char *buf, size_t len) + read(char* buf, size_t len) { return mraa_uart_read(m_uart, buf, len); } @@ -123,7 +123,7 @@ class Uart * @return the number of bytes written, or -1 if an error occurred */ int - write(char *buf, size_t len) + write(char* buf, size_t len) { return mraa_uart_write(m_uart, buf, len); } @@ -134,14 +134,14 @@ class Uart * @param millis number of milliseconds to wait, or 0 to return immediately * @return true if there is data available to read, false otherwise */ - bool - dataAvailable(unsigned int millis=0) - { - if (mraa_uart_data_available(m_uart, millis)) - return true; - else - return false; - } + bool + dataAvailable(unsigned int millis = 0) + { + if (mraa_uart_data_available(m_uart, millis)) + return true; + else + return false; + } private: mraa_uart_context m_uart; diff --git a/src/uart/uart.c b/src/uart/uart.c index bc04ef8..257daef 100644 --- a/src/uart/uart.c +++ b/src/uart/uart.c @@ -1,7 +1,8 @@ /* * Author: Thomas Ingleby * Contributions: Jon Trulson - * Copyright (c) 2014 Intel Corporation. + * Brendan le Foll + * 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 @@ -31,49 +32,6 @@ #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 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; - } - - // 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; -} - mraa_uart_context mraa_uart_init(int index) { @@ -119,16 +77,8 @@ mraa_uart_init(int index) } } - mraa_uart_context dev = (mraa_uart_context) malloc(sizeof(struct _uart)); - if (dev == NULL) { - syslog(LOG_CRIT, "uart: Failed to allocate memory for context"); - return NULL; - } - memset(dev, 0, sizeof(struct _uart)); + mraa_uart_context dev = mraa_uart_init_raw(index); - dev->index = index; - dev->fd = -1; - dev->path = (char*) plat->uart_dev[index].device_path; if (advance_func->uart_init_post != NULL) { mraa_result_t ret = advance_func->uart_init_post(dev); if (ret != MRAA_SUCCESS) { @@ -140,52 +90,105 @@ mraa_uart_init(int index) return dev; } -char* -mraa_uart_get_dev_path(mraa_uart_context dev) +mraa_uart_context +mraa_uart_init_raw(int index) { + mraa_uart_context dev = (mraa_uart_context) malloc(sizeof(struct _uart)); if (dev == NULL) { - syslog(LOG_ERR, "uart: get_device_path failed, context is NULL"); + syslog(LOG_CRIT, "uart: Failed to allocate memory for context"); return NULL; } - if (dev->path == NULL) { - syslog(LOG_ERR, "uart: device path undefined"); - return NULL; - } - return dev->path; -} + memset(dev, 0, sizeof(struct _uart)); -mraa_result_t -mraa_uart_open_dev(mraa_uart_context dev, unsigned int baud) -{ - if (!dev) { - syslog(LOG_ERR, "uart: open: context is NULL"); - return MRAA_ERROR_INVALID_HANDLE; - } + dev->index = index; + dev->fd = -1; + dev->path = (char*) plat->uart_dev[index].device_path; - char *devPath = mraa_uart_get_dev_path(dev); + char* devPath = mraa_uart_get_dev_path(dev); if (!devPath) { syslog(LOG_ERR, "uart: device path undefined, open failed"); - return MRAA_ERROR_UNSPECIFIED; + free(dev); + return NULL; } // now open the device - if ( (dev->fd = open(devPath, O_RDWR)) == -1) { + if ((dev->fd = open(devPath, O_RDWR)) == -1) { syslog(LOG_ERR, "uart: open() failed"); - return MRAA_ERROR_UNSPECIFIED; + free(dev); + return NULL; } // now setup the tty and the selected baud rate - struct termios termio; // get current modes - tcgetattr(dev->fd, &termio); + if (!tcgetattr(dev->fd, &termio)) { + syslog(LOG_ERR, "uart: tcgetattr() failed"); + free(dev); + return NULL; + } - // setup for a 'raw' mode. 81N, no echo or special character + // 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 (!mraa_uart_set_baudrate(dev, 9600)) { + 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)) { + 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); @@ -194,31 +197,145 @@ mraa_uart_open_dev(mraa_uart_context dev, unsigned int baud) // make it so if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) { syslog(LOG_ERR, "uart: tcsetattr() failed"); - return MRAA_ERROR_UNSPECIFIED; + return MRAA_ERROR_FEATURE_NOT_SUPPORTED; } - return MRAA_SUCCESS; } mraa_result_t -mraa_uart_close_dev(mraa_uart_context dev) +mraa_uart_set_mode(mraa_uart_context dev, int bytesize, mraa_uart_parity_t parity, int stopbits) { if (!dev) { - syslog(LOG_ERR, "uart: close: context is NULL"); + 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); + struct termios termio; + if (!tcgetattr(dev->fd, &termio)) { + syslog(LOG_ERR, "uart: tcgetattr() failed"); + return MRAA_ERROR_INVALID_HANDLE; } - dev->fd = -1; - return MRAA_SUCCESS; + 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 &= ~PARODD; + 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; + } +} + +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; + } +} + +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; + } +} + +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) +mraa_uart_read(mraa_uart_context dev, char* buf, size_t len) { if (!dev) { syslog(LOG_ERR, "uart: read: context is NULL"); @@ -234,7 +351,7 @@ mraa_uart_read(mraa_uart_context dev, char *buf, size_t len) } int -mraa_uart_write(mraa_uart_context dev, char *buf, size_t len) +mraa_uart_write(mraa_uart_context dev, char* buf, size_t len) { if (!dev) { syslog(LOG_ERR, "uart: write: context is NULL"); @@ -268,8 +385,7 @@ mraa_uart_data_available(mraa_uart_context dev, unsigned int millis) // no waiting timeout.tv_sec = 0; timeout.tv_usec = 0; - } - else { + } else { timeout.tv_sec = millis / 1000; timeout.tv_usec = (millis % 1000) * 1000; } @@ -279,8 +395,83 @@ mraa_uart_data_available(mraa_uart_context dev, unsigned int millis) FD_ZERO(&readfds); FD_SET(dev->fd, &readfds); - if (select(dev->fd + 1, &readfds, NULL, NULL, &timeout) > 0) - return 1; // data is ready - else + if (select(dev->fd + 1, &readfds, NULL, NULL, &timeout) > 0) { + return 1; // data is ready + } else { return 0; + } +} + +// 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; + } }