blob: c3bf5e89adff2182b535478f69031039f17c1b3e [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
Pau Espin Pedrol23999da2020-11-13 22:57:49 +010070 /* now put it into blocking mode */
Harald Welte1db37822016-12-23 22:49:39 +010071 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
Pau Espin Pedrol28acb212020-11-13 22:58:02 +010094 if (cfsetispeed(&tio, baudrate) < 0)
95 dbg_perror("cfsetispeed()");
96 if (cfsetospeed(&tio, baudrate) < 0)
97 dbg_perror("cfsetospeed()");
Sylvain Munautfe28ded2011-09-02 22:18:24 +020098
99 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
100 tio.c_cflag |= (CREAD | CLOCAL | CS8);
101 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
Harald Welte2e286a52019-09-28 12:59:20 +0200102 tio.c_iflag |= (INPCK);
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200103 tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
104 tio.c_oflag &= ~(OPOST | ONLCR);
105
106 rc = tcsetattr(fd, TCSANOW, &tio);
107 if (rc < 0) {
108 dbg_perror("tcsetattr()");
109 rc = -errno;
110 goto error;
111 }
112
113 /* Set ready to read/write */
114 v24 = TIOCM_DTR | TIOCM_RTS;
115 rc = ioctl(fd, TIOCMBIS, &v24);
116 if (rc < 0) {
117 dbg_perror("ioctl(TIOCMBIS)");
Harald Weltec68ce3b2016-12-23 22:59:27 +0100118 /* some serial porst don't support this, so let's not
119 * return an error here */
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200120 }
121
122 return fd;
123
124error:
Harald Welte1514f342016-12-24 17:59:36 +0100125 if (fd >= 0)
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200126 close(fd);
127 return rc;
128}
129
130static int
131_osmo_serial_set_baudrate(int fd, speed_t baudrate)
132{
133 int rc;
134 struct termios tio;
135
136 rc = tcgetattr(fd, &tio);
137 if (rc < 0) {
138 dbg_perror("tcgetattr()");
139 return -errno;
140 }
Pau Espin Pedrol28acb212020-11-13 22:58:02 +0100141
142 if (cfsetispeed(&tio, baudrate) < 0)
143 dbg_perror("cfsetispeed()");
144 if (cfsetospeed(&tio, baudrate) < 0)
145 dbg_perror("cfsetospeed()");
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200146
147 rc = tcsetattr(fd, TCSANOW, &tio);
148 if (rc < 0) {
Pau Espin Pedrol71ba8732020-11-13 22:58:00 +0100149 dbg_perror("tcsetattr()");
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200150 return -errno;
151 }
152
153 return 0;
154}
155
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200156/*! Change current baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200157 * \param[in] fd File descriptor of the open device
158 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
159 * \returns 0 for success or negative errno.
160 */
161int
162osmo_serial_set_baudrate(int fd, speed_t baudrate)
163{
164 osmo_serial_clear_custom_baudrate(fd);
165 return _osmo_serial_set_baudrate(fd, baudrate);
166}
167
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200168/*! Change current baudrate to a custom one using OS specific method
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200169 * \param[in] fd File descriptor of the open device
170 * \param[in] baudrate Baudrate as integer
171 * \returns 0 for success or negative errno.
172 *
173 * This function might not work on all OS or with all type of serial adapters
174 */
175int
176osmo_serial_set_custom_baudrate(int fd, int baudrate)
177{
Sylvain Munaut96311842011-09-28 09:10:32 +0200178#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200179 int rc;
180 struct serial_struct ser_info;
181
182 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
183 if (rc < 0) {
184 dbg_perror("ioctl(TIOCGSERIAL)");
185 return -errno;
186 }
187
188 ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
189 ser_info.custom_divisor = ser_info.baud_base / baudrate;
190
191 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
192 if (rc < 0) {
193 dbg_perror("ioctl(TIOCSSERIAL)");
194 return -errno;
195 }
196
197 return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */
Sylvain Munaut96311842011-09-28 09:10:32 +0200198#elif defined(__APPLE__)
199#ifndef IOSSIOSPEED
200#define IOSSIOSPEED _IOW('T', 2, speed_t)
201#endif
202 int rc;
203
204 unsigned int speed = baudrate;
205 rc = ioctl(fd, IOSSIOSPEED, &speed);
206 if (rc < 0) {
Sylvain Munaute40549a2011-09-28 10:55:22 +0200207 dbg_perror("ioctl(IOSSIOSPEED)");
Sylvain Munaut96311842011-09-28 09:10:32 +0200208 return -errno;
209 }
210 return 0;
211#else
Pau Espin Pedrolea809df2017-06-23 11:03:55 +0200212#pragma message ("osmo_serial_set_custom_baudrate: unsupported platform")
Sylvain Munaut96311842011-09-28 09:10:32 +0200213 return 0;
214#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200215}
216
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200217/*! Clear any custom baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200218 * \param[in] fd File descriptor of the open device
219 * \returns 0 for success or negative errno.
220 *
221 * This function might not work on all OS or with all type of serial adapters
222 */
223int
224osmo_serial_clear_custom_baudrate(int fd)
225{
Sylvain Munaut96311842011-09-28 09:10:32 +0200226#ifdef __linux__
Sylvain Munaut31d3de92011-11-20 08:55:11 +0100227 int rc;
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200228 struct serial_struct ser_info;
229
230 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
231 if (rc < 0) {
232 dbg_perror("ioctl(TIOCGSERIAL)");
233 return -errno;
234 }
235
236 ser_info.flags = ASYNC_LOW_LATENCY;
237 ser_info.custom_divisor = 0;
238
239 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
240 if (rc < 0) {
241 dbg_perror("ioctl(TIOCSSERIAL)");
242 return -errno;
243 }
Sylvain Munaut96311842011-09-28 09:10:32 +0200244#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200245 return 0;
246}
247
Pau Espin Pedrol4fe8a752020-11-13 22:58:14 +0100248/*! Convert unsigned integer value to speed_t
249 * \param[in] baudrate integer value containing the desired standard baudrate
250 * \param[out] speed the standrd baudrate requested in speed_t format
251 * \returns 0 for success or negative errno.
252 */
253int
254osmo_serial_speed_t(unsigned int baudrate, speed_t *speed)
255{
256 switch(baudrate) {
257 case 0: *speed = B0; break;
258 case 50: *speed = B50; break;
259 case 75: *speed = B75; break;
260 case 110: *speed = B110; break;
261 case 134: *speed = B134; break;
262 case 150: *speed = B150; break;
263 case 200: *speed = B200; break;
264 case 300: *speed = B300; break;
265 case 600: *speed = B600; break;
266 case 1200: *speed = B1200; break;
267 case 1800: *speed = B1800; break;
268 case 2400: *speed = B2400; break;
269 case 4800: *speed = B4800; break;
270 case 9600: *speed = B9600; break;
271 case 19200: *speed = B19200; break;
272 case 38400: *speed = B38400; break;
273 case 57600: *speed = B57600; break;
274 case 115200: *speed = B115200; break;
275 case 230400: *speed = B230400; break;
276 default:
277 *speed = B0;
278 return -EINVAL;
279 }
280 return 0;
281}
282
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200283/*! @} */