blob: 1b5a4cfd65dd43055016708e6154410163493579 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file serial.c
2 * Utility functions to deal with serial ports */
Sylvain Munautfe28ded2011-09-02 22:18:24 +02003/*
Sylvain Munautfe28ded2011-09-02 22:18:24 +02004 * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
5 *
6 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
9 *
Sylvain Munautfe28ded2011-09-02 22:18:24 +020010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 */
24
25/*! \addtogroup serial
26 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020027 * Osmocom serial port helpers
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020028 *
29 * \file serial.c */
Sylvain Munautfe28ded2011-09-02 22:18:24 +020030
31#include <errno.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <termios.h>
35#include <unistd.h>
36#include <sys/ioctl.h>
37#include <sys/types.h>
38#include <sys/stat.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020039#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +020040#include <linux/serial.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020041#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +020042
43#include <osmocom/core/serial.h>
44
45
46#if 0
47# define dbg_perror(x) perror(x)
48#else
49# define dbg_perror(x) do { } while (0)
50#endif
51
Neels Hofmeyr87e45502017-06-20 00:17:59 +020052/*! Open serial device and does base init
Sylvain Munautfe28ded2011-09-02 22:18:24 +020053 * \param[in] dev Path to the device node to open
54 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
55 * \returns >=0 file descriptor in case of success or negative errno.
56 */
57int
58osmo_serial_init(const char *dev, speed_t baudrate)
59{
Harald Welte1514f342016-12-24 17:59:36 +010060 int rc, fd=-1, v24, flags;
Sylvain Munautfe28ded2011-09-02 22:18:24 +020061 struct termios tio;
62
Harald Welte1db37822016-12-23 22:49:39 +010063 /* Use nonblock as the device might block otherwise */
64 fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
Sylvain Munautfe28ded2011-09-02 22:18:24 +020065 if (fd < 0) {
66 dbg_perror("open");
67 return -errno;
68 }
69
Harald Welte1db37822016-12-23 22:49:39 +010070 /* now put it into blcoking mode */
71 flags = fcntl(fd, F_GETFL, 0);
72 if (flags < 0) {
73 dbg_perror("fcntl get flags");
Harald Welted2510452016-12-24 17:58:13 +010074 rc = -errno;
75 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010076 }
77
78 flags &= ~O_NONBLOCK;
79 rc = fcntl(fd, F_SETFL, flags);
80 if (rc != 0) {
81 dbg_perror("fcntl set flags");
Harald Welted2510452016-12-24 17:58:13 +010082 rc = -errno;
83 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010084 }
85
Sylvain Munautfe28ded2011-09-02 22:18:24 +020086 /* Configure serial interface */
87 rc = tcgetattr(fd, &tio);
88 if (rc < 0) {
89 dbg_perror("tcgetattr()");
90 rc = -errno;
91 goto error;
92 }
93
94 cfsetispeed(&tio, baudrate);
95 cfsetospeed(&tio, baudrate);
96
97 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
98 tio.c_cflag |= (CREAD | CLOCAL | CS8);
99 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
100 tio.c_iflag |= (INPCK | ISTRIP);
101 tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
102 tio.c_oflag &= ~(OPOST | ONLCR);
103
104 rc = tcsetattr(fd, TCSANOW, &tio);
105 if (rc < 0) {
106 dbg_perror("tcsetattr()");
107 rc = -errno;
108 goto error;
109 }
110
111 /* Set ready to read/write */
112 v24 = TIOCM_DTR | TIOCM_RTS;
113 rc = ioctl(fd, TIOCMBIS, &v24);
114 if (rc < 0) {
115 dbg_perror("ioctl(TIOCMBIS)");
Harald Weltec68ce3b2016-12-23 22:59:27 +0100116 /* some serial porst don't support this, so let's not
117 * return an error here */
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200118 }
119
120 return fd;
121
122error:
Harald Welte1514f342016-12-24 17:59:36 +0100123 if (fd >= 0)
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200124 close(fd);
125 return rc;
126}
127
128static int
129_osmo_serial_set_baudrate(int fd, speed_t baudrate)
130{
131 int rc;
132 struct termios tio;
133
134 rc = tcgetattr(fd, &tio);
135 if (rc < 0) {
136 dbg_perror("tcgetattr()");
137 return -errno;
138 }
139 cfsetispeed(&tio, baudrate);
140 cfsetospeed(&tio, baudrate);
141
142 rc = tcsetattr(fd, TCSANOW, &tio);
143 if (rc < 0) {
144 dbg_perror("tcgetattr()");
145 return -errno;
146 }
147
148 return 0;
149}
150
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200151/*! Change current baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200152 * \param[in] fd File descriptor of the open device
153 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
154 * \returns 0 for success or negative errno.
155 */
156int
157osmo_serial_set_baudrate(int fd, speed_t baudrate)
158{
159 osmo_serial_clear_custom_baudrate(fd);
160 return _osmo_serial_set_baudrate(fd, baudrate);
161}
162
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200163/*! Change current baudrate to a custom one using OS specific method
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200164 * \param[in] fd File descriptor of the open device
165 * \param[in] baudrate Baudrate as integer
166 * \returns 0 for success or negative errno.
167 *
168 * This function might not work on all OS or with all type of serial adapters
169 */
170int
171osmo_serial_set_custom_baudrate(int fd, int baudrate)
172{
Sylvain Munaut96311842011-09-28 09:10:32 +0200173#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200174 int rc;
175 struct serial_struct ser_info;
176
177 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
178 if (rc < 0) {
179 dbg_perror("ioctl(TIOCGSERIAL)");
180 return -errno;
181 }
182
183 ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
184 ser_info.custom_divisor = ser_info.baud_base / baudrate;
185
186 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
187 if (rc < 0) {
188 dbg_perror("ioctl(TIOCSSERIAL)");
189 return -errno;
190 }
191
192 return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */
Sylvain Munaut96311842011-09-28 09:10:32 +0200193#elif defined(__APPLE__)
194#ifndef IOSSIOSPEED
195#define IOSSIOSPEED _IOW('T', 2, speed_t)
196#endif
197 int rc;
198
199 unsigned int speed = baudrate;
200 rc = ioctl(fd, IOSSIOSPEED, &speed);
201 if (rc < 0) {
Sylvain Munaute40549a2011-09-28 10:55:22 +0200202 dbg_perror("ioctl(IOSSIOSPEED)");
Sylvain Munaut96311842011-09-28 09:10:32 +0200203 return -errno;
204 }
205 return 0;
206#else
Pau Espin Pedrolea809df2017-06-23 11:03:55 +0200207#pragma message ("osmo_serial_set_custom_baudrate: unsupported platform")
Sylvain Munaut96311842011-09-28 09:10:32 +0200208 return 0;
209#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200210}
211
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200212/*! Clear any custom baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200213 * \param[in] fd File descriptor of the open device
214 * \returns 0 for success or negative errno.
215 *
216 * This function might not work on all OS or with all type of serial adapters
217 */
218int
219osmo_serial_clear_custom_baudrate(int fd)
220{
Sylvain Munaut96311842011-09-28 09:10:32 +0200221#ifdef __linux__
Sylvain Munaut31d3de92011-11-20 08:55:11 +0100222 int rc;
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200223 struct serial_struct ser_info;
224
225 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
226 if (rc < 0) {
227 dbg_perror("ioctl(TIOCGSERIAL)");
228 return -errno;
229 }
230
231 ser_info.flags = ASYNC_LOW_LATENCY;
232 ser_info.custom_divisor = 0;
233
234 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
235 if (rc < 0) {
236 dbg_perror("ioctl(TIOCSSERIAL)");
237 return -errno;
238 }
Sylvain Munaut96311842011-09-28 09:10:32 +0200239#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200240 return 0;
241}
242
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200243/*! @} */