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