Home | History | Annotate | Download | only in uart
      1 /*
      2  * Author: Thomas Ingleby <thomas.c.ingleby (at) intel.com>
      3  * Contributions: Jon Trulson <jtrulson (at) ics.com>
      4  *                Brendan le Foll <brendan.le.foll (at) intel.com>
      5  * Copyright (c) 2014 - 2015 Intel Corporation.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining
      8  * a copy of this software and associated documentation files (the
      9  * "Software"), to deal in the Software without restriction, including
     10  * without limitation the rights to use, copy, modify, merge, publish,
     11  * distribute, sublicense, and/or sell copies of the Software, and to
     12  * permit persons to whom the Software is furnished to do so, subject to
     13  * the following conditions:
     14  *
     15  * The above copyright notice and this permission notice shall be
     16  * included in all copies or substantial portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     23  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  */
     26 
     27 #include <stdlib.h>
     28 #include <sys/stat.h>
     29 #include <unistd.h>
     30 #include <string.h>
     31 #include <termios.h>
     32 
     33 #include "uart.h"
     34 #include "mraa_internal.h"
     35 
     36 // This function takes an unsigned int and converts it to a B* speed_t
     37 // that can be used with linux/posix termios
     38 static speed_t
     39 uint2speed(unsigned int speed)
     40 {
     41     switch (speed) {
     42         case 0:
     43             return B0; // hangup, not too useful otherwise
     44         case 50:
     45             return B50;
     46         case 75:
     47             return B75;
     48         case 110:
     49             return B110;
     50         case 150:
     51             return B150;
     52         case 200:
     53             return B200;
     54         case 300:
     55             return B300;
     56         case 600:
     57             return B600;
     58         case 1200:
     59             return B1200;
     60         case 1800:
     61             return B1800;
     62         case 2400:
     63             return B2400;
     64         case 4800:
     65             return B4800;
     66         case 9600:
     67             return B9600;
     68         case 19200:
     69             return B19200;
     70         case 38400:
     71             return B38400;
     72         case 57600:
     73             return B57600;
     74         case 115200:
     75             return B115200;
     76         case 230400:
     77             return B230400;
     78         case 460800:
     79             return B460800;
     80         case 500000:
     81             return B500000;
     82         case 576000:
     83             return B576000;
     84         case 921600:
     85             return B921600;
     86         case 1000000:
     87             return B1000000;
     88         case 1152000:
     89             return B1152000;
     90         case 1500000:
     91             return B1500000;
     92         case 2000000:
     93             return B2000000;
     94         case 2500000:
     95             return B2500000;
     96         case 3000000:
     97             return B3000000;
     98         case 3500000:
     99             return B3500000;
    100         case 4000000:
    101             return B4000000;
    102         default:
    103             // if we are here, then an unsupported baudrate was selected.
    104             // Report it via syslog and return B9600, a common default.
    105             syslog(LOG_ERR, "uart: unsupported baud rate, defaulting to 9600.");
    106             return B9600;
    107     }
    108 }
    109 
    110 static mraa_uart_context
    111 mraa_uart_init_internal(mraa_adv_func_t* func_table)
    112 {
    113     mraa_uart_context dev = (mraa_uart_context) calloc(1, sizeof(struct _uart));
    114     if (dev == NULL) {
    115         syslog(LOG_CRIT, "uart: Failed to allocate memory for context");
    116         return NULL;
    117     }
    118     dev->index = -1;
    119     dev->fd = -1;
    120     dev->advance_func = func_table;
    121 
    122     return dev;
    123 }
    124 
    125 mraa_uart_context
    126 mraa_uart_init(int index)
    127 {
    128     if (plat == NULL) {
    129         syslog(LOG_ERR, "uart: platform not initialised");
    130         return NULL;
    131     }
    132 
    133     if (mraa_is_sub_platform_id(index)) {
    134         syslog(LOG_NOTICE, "uart: Using sub platform is not supported");
    135         return NULL;
    136     }
    137 
    138     if (plat->adv_func->uart_init_pre != NULL) {
    139         if (plat->adv_func->uart_init_pre(index) != MRAA_SUCCESS) {
    140             syslog(LOG_ERR, "uart: failure in pre-init platform hook");
    141             return NULL;
    142         }
    143     }
    144 
    145     if (plat->uart_dev_count == 0) {
    146         syslog(LOG_ERR, "uart: platform has no UARTs defined");
    147         return NULL;
    148     }
    149 
    150     if (plat->uart_dev_count <= index) {
    151         syslog(LOG_ERR, "uart: platform has only %i", plat->uart_dev_count);
    152         return NULL;
    153     }
    154 
    155     if (!plat->no_bus_mux) {
    156         int pos = plat->uart_dev[index].rx;
    157         if (pos >= 0) {
    158             if (plat->pins[pos].uart.mux_total > 0) {
    159                 if (mraa_setup_mux_mapped(plat->pins[pos].uart) != MRAA_SUCCESS) {
    160                     syslog(LOG_ERR, "uart: failed to setup muxes for RX pin");
    161                     return NULL;
    162                 }
    163             }
    164         }
    165 
    166         pos = plat->uart_dev[index].tx;
    167         if (pos >= 0) {
    168             if (plat->pins[pos].uart.mux_total > 0) {
    169                 if (mraa_setup_mux_mapped(plat->pins[pos].uart) != MRAA_SUCCESS) {
    170                     syslog(LOG_ERR, "uart: failed to setup muxes for TX pin");
    171                     return NULL;
    172                 }
    173             }
    174         }
    175     }
    176 
    177     mraa_uart_context dev = mraa_uart_init_raw((char*)plat->uart_dev[index].device_path);
    178     if (dev == NULL) {
    179         return NULL;
    180     }
    181     dev->index = index; //Set the board Index.
    182 
    183     if (IS_FUNC_DEFINED(dev, uart_init_post)) {
    184         mraa_result_t ret = dev->advance_func->uart_init_post(dev);
    185         if (ret != MRAA_SUCCESS) {
    186             free(dev);
    187             return NULL;
    188         }
    189     }
    190 
    191     return dev;
    192 }
    193 
    194 mraa_uart_context
    195 mraa_uart_init_raw(const char* path)
    196 {
    197     mraa_uart_context dev = mraa_uart_init_internal(plat == NULL ? NULL : plat->adv_func);
    198     if (dev == NULL) {
    199         syslog(LOG_ERR, "uart: Failed to allocate memory for context");
    200         return NULL;
    201     }
    202     dev->path = path;
    203 
    204     if (!dev->path) {
    205         syslog(LOG_ERR, "uart: device path undefined, open failed");
    206         free(dev);
    207         return NULL;
    208     }
    209 
    210     // now open the device
    211     if ((dev->fd = open(dev->path, O_RDWR)) == -1) {
    212         syslog(LOG_ERR, "uart: open() failed");
    213         free(dev);
    214         return NULL;
    215     }
    216 
    217     // now setup the tty and the selected baud rate
    218     struct termios termio;
    219 
    220     // get current modes
    221     if (tcgetattr(dev->fd, &termio)) {
    222         syslog(LOG_ERR, "uart: tcgetattr() failed");
    223         close(dev->fd);
    224         free(dev);
    225         return NULL;
    226     }
    227 
    228     // setup for a 'raw' mode.  8N1, no echo or special character
    229     // handling, such as flow control or line editing semantics.
    230     // cfmakeraw is not POSIX!
    231     cfmakeraw(&termio);
    232     if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
    233         syslog(LOG_ERR, "uart: tcsetattr() failed after cfmakeraw()");
    234         close(dev->fd);
    235         free(dev);
    236         return NULL;
    237     }
    238 
    239     if (mraa_uart_set_baudrate(dev, 9600) != MRAA_SUCCESS) {
    240         close(dev->fd);
    241         free(dev);
    242         return NULL;
    243     }
    244 
    245     return dev;
    246 }
    247 
    248 mraa_result_t
    249 mraa_uart_stop(mraa_uart_context dev)
    250 {
    251     if (!dev) {
    252         syslog(LOG_ERR, "uart: stop: context is NULL");
    253         return MRAA_ERROR_INVALID_HANDLE;
    254     }
    255 
    256     // just close the device and reset our fd.
    257     if (dev->fd >= 0) {
    258         close(dev->fd);
    259     }
    260 
    261     free(dev);
    262 
    263     return MRAA_SUCCESS;
    264 }
    265 
    266 mraa_result_t
    267 mraa_uart_flush(mraa_uart_context dev)
    268 {
    269     if (!dev) {
    270         syslog(LOG_ERR, "uart: stop: context is NULL");
    271         return MRAA_ERROR_INVALID_HANDLE;
    272     }
    273 
    274     if (tcdrain(dev->fd) == -1) {
    275         return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
    276     }
    277 
    278     return MRAA_SUCCESS;
    279 }
    280 
    281 mraa_result_t
    282 mraa_uart_set_baudrate(mraa_uart_context dev, unsigned int baud)
    283 {
    284     if (!dev) {
    285         syslog(LOG_ERR, "uart: stop: context is NULL");
    286         return MRAA_ERROR_INVALID_HANDLE;
    287     }
    288 
    289     struct termios termio;
    290     if (tcgetattr(dev->fd, &termio)) {
    291         syslog(LOG_ERR, "uart: tcgetattr() failed");
    292         return MRAA_ERROR_INVALID_HANDLE;
    293     }
    294 
    295     // set our baud rates
    296     speed_t speed = uint2speed(baud);
    297     cfsetispeed(&termio, speed);
    298     cfsetospeed(&termio, speed);
    299 
    300     // make it so
    301     if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
    302         syslog(LOG_ERR, "uart: tcsetattr() failed");
    303         return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
    304     }
    305     return MRAA_SUCCESS;
    306 }
    307 
    308 mraa_result_t
    309 mraa_uart_set_mode(mraa_uart_context dev, int bytesize, mraa_uart_parity_t parity, int stopbits)
    310 {
    311     if (!dev) {
    312         syslog(LOG_ERR, "uart: stop: context is NULL");
    313         return MRAA_ERROR_INVALID_HANDLE;
    314     }
    315 
    316     struct termios termio;
    317     if (tcgetattr(dev->fd, &termio)) {
    318         syslog(LOG_ERR, "uart: tcgetattr() failed");
    319         return MRAA_ERROR_INVALID_HANDLE;
    320     }
    321 
    322     termio.c_cflag &= ~CSIZE;
    323     switch (bytesize) {
    324         case 8:
    325             termio.c_cflag |= CS8;
    326             break;
    327         case 7:
    328             termio.c_cflag |= CS7;
    329             break;
    330         case 6:
    331             termio.c_cflag |= CS6;
    332             break;
    333         case 5:
    334             termio.c_cflag |= CS5;
    335             break;
    336         default:
    337             termio.c_cflag |= CS8;
    338             break;
    339     }
    340 
    341     // POSIX & linux doesn't support 1.5 and I've got bigger fish to fry
    342     switch (stopbits) {
    343         case 1:
    344             termio.c_cflag &= ~CSTOPB;
    345             break;
    346         case 2:
    347             termio.c_cflag |= CSTOPB;
    348         default:
    349             break;
    350     }
    351 
    352     switch (parity) {
    353         case MRAA_UART_PARITY_NONE:
    354             termio.c_cflag &= ~(PARENB | PARODD);
    355             break;
    356         case MRAA_UART_PARITY_EVEN:
    357             termio.c_cflag |= PARENB;
    358             termio.c_cflag &= ~PARODD;
    359             break;
    360         case MRAA_UART_PARITY_ODD:
    361             termio.c_cflag |= PARENB | PARODD;
    362             break;
    363         case MRAA_UART_PARITY_MARK: // not POSIX
    364             termio.c_cflag |= PARENB | CMSPAR | PARODD;
    365             break;
    366         case MRAA_UART_PARITY_SPACE: // not POSIX
    367             termio.c_cflag |= PARENB | CMSPAR;
    368             termio.c_cflag &= ~PARODD;
    369             break;
    370     }
    371 
    372     if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
    373         syslog(LOG_ERR, "uart: tcsetattr() failed");
    374         return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
    375     }
    376 
    377     return MRAA_SUCCESS;
    378 }
    379 
    380 mraa_result_t
    381 mraa_uart_set_flowcontrol(mraa_uart_context dev, mraa_boolean_t xonxoff, mraa_boolean_t rtscts)
    382 {
    383     if (!dev) {
    384         syslog(LOG_ERR, "uart: stop: context is NULL");
    385         return MRAA_ERROR_INVALID_HANDLE;
    386     }
    387 
    388     // hardware flow control
    389     int action = TCIOFF;
    390     if (xonxoff) {
    391         action = TCION;
    392     }
    393     if (tcflow(dev->fd, action)) {
    394         return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
    395     }
    396 
    397     // rtscts
    398     struct termios termio;
    399 
    400     // get current modes
    401     if (tcgetattr(dev->fd, &termio)) {
    402         syslog(LOG_ERR, "uart: tcgetattr() failed");
    403         return MRAA_ERROR_INVALID_HANDLE;
    404     }
    405 
    406     if (rtscts) {
    407         termio.c_cflag |= CRTSCTS;
    408     } else {
    409         termio.c_cflag &= ~CRTSCTS;
    410     }
    411 
    412     if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
    413         syslog(LOG_ERR, "uart: tcsetattr() failed");
    414         return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
    415     }
    416 
    417     return MRAA_SUCCESS;
    418 }
    419 
    420 mraa_result_t
    421 mraa_uart_set_timeout(mraa_uart_context dev, int read, int write, int interchar)
    422 {
    423     if (!dev) {
    424         syslog(LOG_ERR, "uart: stop: context is NULL");
    425         return MRAA_ERROR_INVALID_HANDLE;
    426     }
    427 
    428     struct termios termio;
    429     // get current modes
    430     if (tcgetattr(dev->fd, &termio)) {
    431         syslog(LOG_ERR, "uart: tcgetattr() failed");
    432         return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
    433     }
    434     if (read > 0) {
    435         read = read / 100;
    436         if (read == 0)
    437             read = 1;
    438     }
    439     termio.c_lflag &= ~ICANON; /* Set non-canonical mode */
    440     termio.c_cc[VTIME] = read; /* Set timeout in tenth seconds */
    441     if (tcsetattr(dev->fd, TCSANOW, &termio) < 0) {
    442         syslog(LOG_ERR, "uart: tcsetattr() failed");
    443         return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
    444     }
    445 
    446     return MRAA_SUCCESS;
    447 }
    448 
    449 const char*
    450 mraa_uart_get_dev_path(mraa_uart_context dev)
    451 {
    452     if (!dev) {
    453         syslog(LOG_ERR, "uart: get_device_path failed, context is NULL");
    454         return NULL;
    455     }
    456     if (dev->path == NULL) {
    457         syslog(LOG_ERR, "uart: device path undefined");
    458         return NULL;
    459     }
    460 
    461     return dev->path;
    462 }
    463 
    464 int
    465 mraa_uart_read(mraa_uart_context dev, char* buf, size_t len)
    466 {
    467     if (!dev) {
    468         syslog(LOG_ERR, "uart: read: context is NULL");
    469         return MRAA_ERROR_INVALID_HANDLE;
    470     }
    471 
    472     if (dev->fd < 0) {
    473         syslog(LOG_ERR, "uart: port is not open");
    474         return MRAA_ERROR_INVALID_RESOURCE;
    475     }
    476 
    477     return read(dev->fd, buf, len);
    478 }
    479 
    480 int
    481 mraa_uart_write(mraa_uart_context dev, const char* buf, size_t len)
    482 {
    483     if (!dev) {
    484         syslog(LOG_ERR, "uart: write: context is NULL");
    485         return MRAA_ERROR_INVALID_HANDLE;
    486     }
    487 
    488     if (dev->fd < 0) {
    489         syslog(LOG_ERR, "uart: port is not open");
    490         return MRAA_ERROR_INVALID_RESOURCE;
    491     }
    492 
    493     return write(dev->fd, buf, len);
    494 }
    495 
    496 mraa_boolean_t
    497 mraa_uart_data_available(mraa_uart_context dev, unsigned int millis)
    498 {
    499     if (!dev) {
    500         syslog(LOG_ERR, "uart: data_available: write context is NULL");
    501         return 0;
    502     }
    503 
    504     if (dev->fd < 0) {
    505         syslog(LOG_ERR, "uart: port is not open");
    506         return 0;
    507     }
    508 
    509     struct timeval timeout;
    510 
    511     if (millis == 0) {
    512         // no waiting
    513         timeout.tv_sec = 0;
    514         timeout.tv_usec = 0;
    515     } else {
    516         timeout.tv_sec = millis / 1000;
    517         timeout.tv_usec = (millis % 1000) * 1000;
    518     }
    519 
    520     fd_set readfds;
    521 
    522     FD_ZERO(&readfds);
    523     FD_SET(dev->fd, &readfds);
    524 
    525     if (select(dev->fd + 1, &readfds, NULL, NULL, &timeout) > 0) {
    526         return 1; // data is ready
    527     } else {
    528         return 0;
    529     }
    530 }
    531 
    532 
    533