uart.c: make use of no_bus_mux flag
[contrib/mraa.git] / src / uart / uart.c
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
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     return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED;
429 }
430
431 const char*
432 mraa_uart_get_dev_path(mraa_uart_context dev)
433 {
434     if (!dev) {
435         syslog(LOG_ERR, "uart: get_device_path failed, context is NULL");
436         return NULL;
437     }
438     if (dev->path == NULL) {
439         syslog(LOG_ERR, "uart: device path undefined");
440         return NULL;
441     }
442
443     return dev->path;
444 }
445
446 int
447 mraa_uart_read(mraa_uart_context dev, char* buf, size_t len)
448 {
449     if (!dev) {
450         syslog(LOG_ERR, "uart: read: context is NULL");
451         return MRAA_ERROR_INVALID_HANDLE;
452     }
453
454     if (dev->fd < 0) {
455         syslog(LOG_ERR, "uart: port is not open");
456         return MRAA_ERROR_INVALID_RESOURCE;
457     }
458
459     return read(dev->fd, buf, len);
460 }
461
462 int
463 mraa_uart_write(mraa_uart_context dev, const char* buf, size_t len)
464 {
465     if (!dev) {
466         syslog(LOG_ERR, "uart: write: context is NULL");
467         return MRAA_ERROR_INVALID_HANDLE;
468     }
469
470     if (dev->fd < 0) {
471         syslog(LOG_ERR, "uart: port is not open");
472         return MRAA_ERROR_INVALID_RESOURCE;
473     }
474
475     return write(dev->fd, buf, len);
476 }
477
478 mraa_boolean_t
479 mraa_uart_data_available(mraa_uart_context dev, unsigned int millis)
480 {
481     if (!dev) {
482         syslog(LOG_ERR, "uart: data_available: write context is NULL");
483         return 0;
484     }
485
486     if (dev->fd < 0) {
487         syslog(LOG_ERR, "uart: port is not open");
488         return 0;
489     }
490
491     struct timeval timeout;
492
493     if (millis == 0) {
494         // no waiting
495         timeout.tv_sec = 0;
496         timeout.tv_usec = 0;
497     } else {
498         timeout.tv_sec = millis / 1000;
499         timeout.tv_usec = (millis % 1000) * 1000;
500     }
501
502     fd_set readfds;
503
504     FD_ZERO(&readfds);
505     FD_SET(dev->fd, &readfds);
506
507     if (select(dev->fd + 1, &readfds, NULL, NULL, &timeout) > 0) {
508         return 1; // data is ready
509     } else {
510         return 0;
511     }
512 }
513
514