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
uint2speed(unsigned int speed)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
mraa_uart_init_internal(mraa_adv_func_t * func_table)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
mraa_uart_init(int index)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
mraa_uart_init_raw(const char * path)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
mraa_uart_stop(mraa_uart_context dev)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
mraa_uart_flush(mraa_uart_context dev)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
mraa_uart_set_baudrate(mraa_uart_context dev,unsigned int baud)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
mraa_uart_set_mode(mraa_uart_context dev,int bytesize,mraa_uart_parity_t parity,int stopbits)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
mraa_uart_set_flowcontrol(mraa_uart_context dev,mraa_boolean_t xonxoff,mraa_boolean_t rtscts)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
mraa_uart_set_timeout(mraa_uart_context dev,int read,int write,int interchar)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*
mraa_uart_get_dev_path(mraa_uart_context dev)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
mraa_uart_read(mraa_uart_context dev,char * buf,size_t len)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
mraa_uart_write(mraa_uart_context dev,const char * buf,size_t len)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
mraa_uart_data_available(mraa_uart_context dev,unsigned int millis)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