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