uart.c revision 7d3978fad94b70b4cb0dba9231b30870022f8563
1/*
2 * Author: Thomas Ingleby <thomas.c.ingleby@intel.com>
3 * Contributions: Jon Trulson <jtrulson@ics.com>
4 *                Brendan le Foll <brendan.le.foll@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
38static speed_t
39uint2speed(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
110static mraa_uart_context
111mraa_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
125mraa_uart_context
126mraa_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    int pos = plat->uart_dev[index].rx;
156    if (pos >= 0) {
157        if (plat->pins[pos].uart.mux_total > 0) {
158            if (mraa_setup_mux_mapped(plat->pins[pos].uart) != MRAA_SUCCESS) {
159                syslog(LOG_ERR, "uart: failed to setup muxes for RX pin");
160                return NULL;
161            }
162        }
163    }
164
165    pos = plat->uart_dev[index].tx;
166    if (pos >= 0) {
167        if (plat->pins[pos].uart.mux_total > 0) {
168            if (mraa_setup_mux_mapped(plat->pins[pos].uart) != MRAA_SUCCESS) {
169                syslog(LOG_ERR, "uart: failed to setup muxes for TX pin");
170                return NULL;
171            }
172        }
173    }
174
175    mraa_uart_context dev = mraa_uart_init_raw((char*)plat->uart_dev[index].device_path);
176    if (dev == NULL) {
177        return NULL;
178    }
179    dev->index = index; //Set the board Index.
180
181    if (IS_FUNC_DEFINED(dev, uart_init_post)) {
182        mraa_result_t ret = dev->advance_func->uart_init_post(dev);
183        if (ret != MRAA_SUCCESS) {
184            free(dev);
185            return NULL;
186        }
187    }
188
189    return dev;
190}
191
192mraa_uart_context
193mraa_uart_init_raw(const char* path)
194{
195    mraa_uart_context dev = mraa_uart_init_internal(plat == NULL ? NULL : plat->adv_func);
196    if (dev == NULL) {
197        syslog(LOG_ERR, "uart: Failed to allocate memory for context");
198        return NULL;
199    }
200    dev->path = path;
201
202    if (!dev->path) {
203        syslog(LOG_ERR, "uart: device path undefined, open failed");
204        free(dev);
205        return NULL;
206    }
207
208    // now open the device
209    if ((dev->fd = open(dev->path, O_RDWR)) == -1) {
210        syslog(LOG_ERR, "uart: open() failed");
211        free(dev);
212        return NULL;
213    }
214
215    // now setup the tty and the selected baud rate
216    struct termios termio;
217
218    // get current modes
219    if (tcgetattr(dev->fd, &termio)) {
220        syslog(LOG_ERR, "uart: tcgetattr() failed");
221        close(dev->fd);
222        free(dev);
223        return NULL;
224    }
225
226    // setup for a 'raw' mode.  8N1, no echo or special character
227    // handling, such as flow control or line editing semantics.
228    // cfmakeraw is not POSIX!
229    cfmakeraw(&termio);
230    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
231        syslog(LOG_ERR, "uart: tcsetattr() failed after cfmakeraw()");
232        close(dev->fd);
233        free(dev);
234        return NULL;
235    }
236
237    if (mraa_uart_set_baudrate(dev, 9600) != MRAA_SUCCESS) {
238        close(dev->fd);
239        free(dev);
240        return NULL;
241    }
242
243    return dev;
244}
245
246mraa_result_t
247mraa_uart_stop(mraa_uart_context dev)
248{
249    if (!dev) {
250        syslog(LOG_ERR, "uart: stop: context is NULL");
251        return MRAA_ERROR_INVALID_HANDLE;
252    }
253
254    // just close the device and reset our fd.
255    if (dev->fd >= 0) {
256        close(dev->fd);
257    }
258
259    free(dev);
260
261    return MRAA_SUCCESS;
262}
263
264mraa_result_t
265mraa_uart_flush(mraa_uart_context dev)
266{
267    if (!dev) {
268        syslog(LOG_ERR, "uart: stop: context is NULL");
269        return MRAA_ERROR_INVALID_HANDLE;
270    }
271
272    if (tcdrain(dev->fd) == -1) {
273        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
274    }
275
276    return MRAA_SUCCESS;
277}
278
279mraa_result_t
280mraa_uart_set_baudrate(mraa_uart_context dev, unsigned int baud)
281{
282    if (!dev) {
283        syslog(LOG_ERR, "uart: stop: context is NULL");
284        return MRAA_ERROR_INVALID_HANDLE;
285    }
286
287    struct termios termio;
288    if (tcgetattr(dev->fd, &termio)) {
289        syslog(LOG_ERR, "uart: tcgetattr() failed");
290        return MRAA_ERROR_INVALID_HANDLE;
291    }
292
293    // set our baud rates
294    speed_t speed = uint2speed(baud);
295    cfsetispeed(&termio, speed);
296    cfsetospeed(&termio, speed);
297
298    // make it so
299    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
300        syslog(LOG_ERR, "uart: tcsetattr() failed");
301        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
302    }
303    return MRAA_SUCCESS;
304}
305
306mraa_result_t
307mraa_uart_set_mode(mraa_uart_context dev, int bytesize, mraa_uart_parity_t parity, int stopbits)
308{
309    if (!dev) {
310        syslog(LOG_ERR, "uart: stop: context is NULL");
311        return MRAA_ERROR_INVALID_HANDLE;
312    }
313
314    struct termios termio;
315    if (tcgetattr(dev->fd, &termio)) {
316        syslog(LOG_ERR, "uart: tcgetattr() failed");
317        return MRAA_ERROR_INVALID_HANDLE;
318    }
319
320    termio.c_cflag &= ~CSIZE;
321    switch (bytesize) {
322        case 8:
323            termio.c_cflag |= CS8;
324            break;
325        case 7:
326            termio.c_cflag |= CS7;
327            break;
328        case 6:
329            termio.c_cflag |= CS6;
330            break;
331        case 5:
332            termio.c_cflag |= CS5;
333            break;
334        default:
335            termio.c_cflag |= CS8;
336            break;
337    }
338
339    // POSIX & linux doesn't support 1.5 and I've got bigger fish to fry
340    switch (stopbits) {
341        case 1:
342            termio.c_cflag &= ~CSTOPB;
343            break;
344        case 2:
345            termio.c_cflag |= CSTOPB;
346        default:
347            break;
348    }
349
350    switch (parity) {
351        case MRAA_UART_PARITY_NONE:
352            termio.c_cflag &= ~(PARENB | PARODD);
353            break;
354        case MRAA_UART_PARITY_EVEN:
355            termio.c_cflag |= PARENB;
356            termio.c_cflag &= ~PARODD;
357            break;
358        case MRAA_UART_PARITY_ODD:
359            termio.c_cflag |= PARENB | PARODD;
360            break;
361        case MRAA_UART_PARITY_MARK: // not POSIX
362            termio.c_cflag |= PARENB | CMSPAR | PARODD;
363            break;
364        case MRAA_UART_PARITY_SPACE: // not POSIX
365            termio.c_cflag |= PARENB | CMSPAR;
366            termio.c_cflag &= ~PARODD;
367            break;
368    }
369
370    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
371        syslog(LOG_ERR, "uart: tcsetattr() failed");
372        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
373    }
374
375    return MRAA_SUCCESS;
376}
377
378mraa_result_t
379mraa_uart_set_flowcontrol(mraa_uart_context dev, mraa_boolean_t xonxoff, mraa_boolean_t rtscts)
380{
381    if (!dev) {
382        syslog(LOG_ERR, "uart: stop: context is NULL");
383        return MRAA_ERROR_INVALID_HANDLE;
384    }
385
386    // hardware flow control
387    int action = TCIOFF;
388    if (xonxoff) {
389        action = TCION;
390    }
391    if (tcflow(dev->fd, action)) {
392        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
393    }
394
395    // rtscts
396    struct termios termio;
397
398    // get current modes
399    if (tcgetattr(dev->fd, &termio)) {
400        syslog(LOG_ERR, "uart: tcgetattr() failed");
401        return MRAA_ERROR_INVALID_HANDLE;
402    }
403
404    if (rtscts) {
405        termio.c_cflag |= CRTSCTS;
406    } else {
407        termio.c_cflag &= ~CRTSCTS;
408    }
409
410    if (tcsetattr(dev->fd, TCSAFLUSH, &termio) < 0) {
411        syslog(LOG_ERR, "uart: tcsetattr() failed");
412        return MRAA_ERROR_FEATURE_NOT_SUPPORTED;
413    }
414
415    return MRAA_SUCCESS;
416}
417
418mraa_result_t
419mraa_uart_set_timeout(mraa_uart_context dev, int read, int write, int interchar)
420{
421    if (!dev) {
422        syslog(LOG_ERR, "uart: stop: context is NULL");
423        return MRAA_ERROR_INVALID_HANDLE;
424    }
425
426    return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED;
427}
428
429const char*
430mraa_uart_get_dev_path(mraa_uart_context dev)
431{
432    if (!dev) {
433        syslog(LOG_ERR, "uart: get_device_path failed, context is NULL");
434        return NULL;
435    }
436    if (dev->path == NULL) {
437        syslog(LOG_ERR, "uart: device path undefined");
438        return NULL;
439    }
440
441    return dev->path;
442}
443
444int
445mraa_uart_read(mraa_uart_context dev, char* buf, size_t len)
446{
447    if (!dev) {
448        syslog(LOG_ERR, "uart: read: context is NULL");
449        return MRAA_ERROR_INVALID_HANDLE;
450    }
451
452    if (dev->fd < 0) {
453        syslog(LOG_ERR, "uart: port is not open");
454        return MRAA_ERROR_INVALID_RESOURCE;
455    }
456
457    return read(dev->fd, buf, len);
458}
459
460int
461mraa_uart_write(mraa_uart_context dev, const char* buf, size_t len)
462{
463    if (!dev) {
464        syslog(LOG_ERR, "uart: write: context is NULL");
465        return MRAA_ERROR_INVALID_HANDLE;
466    }
467
468    if (dev->fd < 0) {
469        syslog(LOG_ERR, "uart: port is not open");
470        return MRAA_ERROR_INVALID_RESOURCE;
471    }
472
473    return write(dev->fd, buf, len);
474}
475
476mraa_boolean_t
477mraa_uart_data_available(mraa_uart_context dev, unsigned int millis)
478{
479    if (!dev) {
480        syslog(LOG_ERR, "uart: data_available: write context is NULL");
481        return 0;
482    }
483
484    if (dev->fd < 0) {
485        syslog(LOG_ERR, "uart: port is not open");
486        return 0;
487    }
488
489    struct timeval timeout;
490
491    if (millis == 0) {
492        // no waiting
493        timeout.tv_sec = 0;
494        timeout.tv_usec = 0;
495    } else {
496        timeout.tv_sec = millis / 1000;
497        timeout.tv_usec = (millis % 1000) * 1000;
498    }
499
500    fd_set readfds;
501
502    FD_ZERO(&readfds);
503    FD_SET(dev->fd, &readfds);
504
505    if (select(dev->fd + 1, &readfds, NULL, NULL, &timeout) > 0) {
506        return 1; // data is ready
507    } else {
508        return 0;
509    }
510}
511
512
513