5fc0cc16a61fc02f88f8305227aa17a82724e702
[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     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
192 mraa_uart_context
193 mraa_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
246 mraa_result_t
247 mraa_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
264 mraa_result_t
265 mraa_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
279 mraa_result_t
280 mraa_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
306 mraa_result_t
307 mraa_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
378 mraa_result_t
379 mraa_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
418 mraa_result_t
419 mraa_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
429 const char*
430 mraa_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
444 int
445 mraa_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
460 int
461 mraa_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
476 mraa_boolean_t
477 mraa_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