blob: 117c049b24b97ca5e64c58abebec5c1e163e0f25 [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.
Sylvain Munautfe28ded2011-09-02 22:18:24 +020019 */
20
21/*! \addtogroup serial
22 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020023 * Osmocom serial port helpers
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020024 *
25 * \file serial.c */
Sylvain Munautfe28ded2011-09-02 22:18:24 +020026
27#include <errno.h>
28#include <fcntl.h>
29#include <stdio.h>
30#include <termios.h>
31#include <unistd.h>
32#include <sys/ioctl.h>
33#include <sys/types.h>
34#include <sys/stat.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020035#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +020036#include <linux/serial.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020037#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +020038
39#include <osmocom/core/serial.h>
40
41
42#if 0
43# define dbg_perror(x) perror(x)
44#else
45# define dbg_perror(x) do { } while (0)
46#endif
47
Neels Hofmeyr87e45502017-06-20 00:17:59 +020048/*! Open serial device and does base init
Sylvain Munautfe28ded2011-09-02 22:18:24 +020049 * \param[in] dev Path to the device node to open
50 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
51 * \returns >=0 file descriptor in case of success or negative errno.
52 */
53int
54osmo_serial_init(const char *dev, speed_t baudrate)
55{
Harald Welte1514f342016-12-24 17:59:36 +010056 int rc, fd=-1, v24, flags;
Sylvain Munautfe28ded2011-09-02 22:18:24 +020057 struct termios tio;
58
Harald Welte1db37822016-12-23 22:49:39 +010059 /* Use nonblock as the device might block otherwise */
60 fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
Sylvain Munautfe28ded2011-09-02 22:18:24 +020061 if (fd < 0) {
62 dbg_perror("open");
63 return -errno;
64 }
65
Pau Espin Pedrol23999da2020-11-13 22:57:49 +010066 /* now put it into blocking mode */
Harald Welte1db37822016-12-23 22:49:39 +010067 flags = fcntl(fd, F_GETFL, 0);
68 if (flags < 0) {
69 dbg_perror("fcntl get flags");
Harald Welted2510452016-12-24 17:58:13 +010070 rc = -errno;
71 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010072 }
73
74 flags &= ~O_NONBLOCK;
75 rc = fcntl(fd, F_SETFL, flags);
76 if (rc != 0) {
77 dbg_perror("fcntl set flags");
Harald Welted2510452016-12-24 17:58:13 +010078 rc = -errno;
79 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010080 }
81
Sylvain Munautfe28ded2011-09-02 22:18:24 +020082 /* Configure serial interface */
83 rc = tcgetattr(fd, &tio);
84 if (rc < 0) {
85 dbg_perror("tcgetattr()");
86 rc = -errno;
87 goto error;
88 }
89
Pau Espin Pedrol28acb212020-11-13 22:58:02 +010090 if (cfsetispeed(&tio, baudrate) < 0)
91 dbg_perror("cfsetispeed()");
92 if (cfsetospeed(&tio, baudrate) < 0)
93 dbg_perror("cfsetospeed()");
Sylvain Munautfe28ded2011-09-02 22:18:24 +020094
95 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
96 tio.c_cflag |= (CREAD | CLOCAL | CS8);
97 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
Harald Welte2e286a52019-09-28 12:59:20 +020098 tio.c_iflag |= (INPCK);
Sylvain Munautfe28ded2011-09-02 22:18:24 +020099 tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
100 tio.c_oflag &= ~(OPOST | ONLCR);
101
102 rc = tcsetattr(fd, TCSANOW, &tio);
103 if (rc < 0) {
104 dbg_perror("tcsetattr()");
105 rc = -errno;
106 goto error;
107 }
108
109 /* Set ready to read/write */
110 v24 = TIOCM_DTR | TIOCM_RTS;
111 rc = ioctl(fd, TIOCMBIS, &v24);
112 if (rc < 0) {
113 dbg_perror("ioctl(TIOCMBIS)");
Harald Weltec68ce3b2016-12-23 22:59:27 +0100114 /* some serial porst don't support this, so let's not
115 * return an error here */
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200116 }
117
118 return fd;
119
120error:
Harald Welte1514f342016-12-24 17:59:36 +0100121 if (fd >= 0)
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200122 close(fd);
123 return rc;
124}
125
126static int
127_osmo_serial_set_baudrate(int fd, speed_t baudrate)
128{
129 int rc;
130 struct termios tio;
131
132 rc = tcgetattr(fd, &tio);
133 if (rc < 0) {
134 dbg_perror("tcgetattr()");
135 return -errno;
136 }
Pau Espin Pedrol28acb212020-11-13 22:58:02 +0100137
138 if (cfsetispeed(&tio, baudrate) < 0)
139 dbg_perror("cfsetispeed()");
140 if (cfsetospeed(&tio, baudrate) < 0)
141 dbg_perror("cfsetospeed()");
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200142
143 rc = tcsetattr(fd, TCSANOW, &tio);
144 if (rc < 0) {
Pau Espin Pedrol71ba8732020-11-13 22:58:00 +0100145 dbg_perror("tcsetattr()");
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200146 return -errno;
147 }
148
149 return 0;
150}
151
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200152/*! Change current baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200153 * \param[in] fd File descriptor of the open device
154 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
155 * \returns 0 for success or negative errno.
156 */
157int
158osmo_serial_set_baudrate(int fd, speed_t baudrate)
159{
160 osmo_serial_clear_custom_baudrate(fd);
161 return _osmo_serial_set_baudrate(fd, baudrate);
162}
163
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200164/*! Change current baudrate to a custom one using OS specific method
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200165 * \param[in] fd File descriptor of the open device
166 * \param[in] baudrate Baudrate as integer
167 * \returns 0 for success or negative errno.
168 *
169 * This function might not work on all OS or with all type of serial adapters
170 */
171int
172osmo_serial_set_custom_baudrate(int fd, int baudrate)
173{
Sylvain Munaut96311842011-09-28 09:10:32 +0200174#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200175 int rc;
176 struct serial_struct ser_info;
177
178 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
179 if (rc < 0) {
180 dbg_perror("ioctl(TIOCGSERIAL)");
181 return -errno;
182 }
183
184 ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
185 ser_info.custom_divisor = ser_info.baud_base / baudrate;
186
187 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
188 if (rc < 0) {
189 dbg_perror("ioctl(TIOCSSERIAL)");
190 return -errno;
191 }
192
193 return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */
Sylvain Munaut96311842011-09-28 09:10:32 +0200194#elif defined(__APPLE__)
195#ifndef IOSSIOSPEED
196#define IOSSIOSPEED _IOW('T', 2, speed_t)
197#endif
198 int rc;
199
200 unsigned int speed = baudrate;
201 rc = ioctl(fd, IOSSIOSPEED, &speed);
202 if (rc < 0) {
Sylvain Munaute40549a2011-09-28 10:55:22 +0200203 dbg_perror("ioctl(IOSSIOSPEED)");
Sylvain Munaut96311842011-09-28 09:10:32 +0200204 return -errno;
205 }
206 return 0;
207#else
Pau Espin Pedrolea809df2017-06-23 11:03:55 +0200208#pragma message ("osmo_serial_set_custom_baudrate: unsupported platform")
Sylvain Munaut96311842011-09-28 09:10:32 +0200209 return 0;
210#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200211}
212
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200213/*! Clear any custom baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200214 * \param[in] fd File descriptor of the open device
215 * \returns 0 for success or negative errno.
216 *
217 * This function might not work on all OS or with all type of serial adapters
218 */
219int
220osmo_serial_clear_custom_baudrate(int fd)
221{
Sylvain Munaut96311842011-09-28 09:10:32 +0200222#ifdef __linux__
Sylvain Munaut31d3de92011-11-20 08:55:11 +0100223 int rc;
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200224 struct serial_struct ser_info;
225
226 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
227 if (rc < 0) {
228 dbg_perror("ioctl(TIOCGSERIAL)");
229 return -errno;
230 }
231
232 ser_info.flags = ASYNC_LOW_LATENCY;
233 ser_info.custom_divisor = 0;
234
235 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
236 if (rc < 0) {
237 dbg_perror("ioctl(TIOCSSERIAL)");
238 return -errno;
239 }
Sylvain Munaut96311842011-09-28 09:10:32 +0200240#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200241 return 0;
242}
243
Pau Espin Pedrol4fe8a752020-11-13 22:58:14 +0100244/*! Convert unsigned integer value to speed_t
245 * \param[in] baudrate integer value containing the desired standard baudrate
246 * \param[out] speed the standrd baudrate requested in speed_t format
247 * \returns 0 for success or negative errno.
248 */
249int
250osmo_serial_speed_t(unsigned int baudrate, speed_t *speed)
251{
252 switch(baudrate) {
253 case 0: *speed = B0; break;
254 case 50: *speed = B50; break;
255 case 75: *speed = B75; break;
256 case 110: *speed = B110; break;
257 case 134: *speed = B134; break;
258 case 150: *speed = B150; break;
259 case 200: *speed = B200; break;
260 case 300: *speed = B300; break;
261 case 600: *speed = B600; break;
262 case 1200: *speed = B1200; break;
263 case 1800: *speed = B1800; break;
264 case 2400: *speed = B2400; break;
265 case 4800: *speed = B4800; break;
266 case 9600: *speed = B9600; break;
267 case 19200: *speed = B19200; break;
268 case 38400: *speed = B38400; break;
269 case 57600: *speed = B57600; break;
270 case 115200: *speed = B115200; break;
271 case 230400: *speed = B230400; break;
272 default:
273 *speed = B0;
274 return -EINVAL;
275 }
276 return 0;
277}
278
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200279/*! @} */