blob: 8ddd38e851913682a0191ffc282b5803b1ca17e8 [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 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23/*! \addtogroup serial
24 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020025 * Osmocom serial port helpers
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020026 *
27 * \file serial.c */
Sylvain Munautfe28ded2011-09-02 22:18:24 +020028
29#include <errno.h>
30#include <fcntl.h>
31#include <stdio.h>
32#include <termios.h>
33#include <unistd.h>
34#include <sys/ioctl.h>
35#include <sys/types.h>
36#include <sys/stat.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020037#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +020038#include <linux/serial.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020039#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +020040
41#include <osmocom/core/serial.h>
42
43
44#if 0
45# define dbg_perror(x) perror(x)
46#else
47# define dbg_perror(x) do { } while (0)
48#endif
49
Neels Hofmeyr87e45502017-06-20 00:17:59 +020050/*! Open serial device and does base init
Sylvain Munautfe28ded2011-09-02 22:18:24 +020051 * \param[in] dev Path to the device node to open
52 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
53 * \returns >=0 file descriptor in case of success or negative errno.
54 */
55int
56osmo_serial_init(const char *dev, speed_t baudrate)
57{
Harald Welte1514f342016-12-24 17:59:36 +010058 int rc, fd=-1, v24, flags;
Sylvain Munautfe28ded2011-09-02 22:18:24 +020059 struct termios tio;
60
Harald Welte1db37822016-12-23 22:49:39 +010061 /* Use nonblock as the device might block otherwise */
62 fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
Sylvain Munautfe28ded2011-09-02 22:18:24 +020063 if (fd < 0) {
64 dbg_perror("open");
65 return -errno;
66 }
67
Harald Welte1db37822016-12-23 22:49:39 +010068 /* now put it into blcoking mode */
69 flags = fcntl(fd, F_GETFL, 0);
70 if (flags < 0) {
71 dbg_perror("fcntl get flags");
Harald Welted2510452016-12-24 17:58:13 +010072 rc = -errno;
73 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010074 }
75
76 flags &= ~O_NONBLOCK;
77 rc = fcntl(fd, F_SETFL, flags);
78 if (rc != 0) {
79 dbg_perror("fcntl set flags");
Harald Welted2510452016-12-24 17:58:13 +010080 rc = -errno;
81 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010082 }
83
Sylvain Munautfe28ded2011-09-02 22:18:24 +020084 /* Configure serial interface */
85 rc = tcgetattr(fd, &tio);
86 if (rc < 0) {
87 dbg_perror("tcgetattr()");
88 rc = -errno;
89 goto error;
90 }
91
92 cfsetispeed(&tio, baudrate);
93 cfsetospeed(&tio, baudrate);
94
95 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
96 tio.c_cflag |= (CREAD | CLOCAL | CS8);
97 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
98 tio.c_iflag |= (INPCK | ISTRIP);
99 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 }
137 cfsetispeed(&tio, baudrate);
138 cfsetospeed(&tio, baudrate);
139
140 rc = tcsetattr(fd, TCSANOW, &tio);
141 if (rc < 0) {
142 dbg_perror("tcgetattr()");
143 return -errno;
144 }
145
146 return 0;
147}
148
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200149/*! Change current baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200150 * \param[in] fd File descriptor of the open device
151 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
152 * \returns 0 for success or negative errno.
153 */
154int
155osmo_serial_set_baudrate(int fd, speed_t baudrate)
156{
157 osmo_serial_clear_custom_baudrate(fd);
158 return _osmo_serial_set_baudrate(fd, baudrate);
159}
160
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200161/*! Change current baudrate to a custom one using OS specific method
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200162 * \param[in] fd File descriptor of the open device
163 * \param[in] baudrate Baudrate as integer
164 * \returns 0 for success or negative errno.
165 *
166 * This function might not work on all OS or with all type of serial adapters
167 */
168int
169osmo_serial_set_custom_baudrate(int fd, int baudrate)
170{
Sylvain Munaut96311842011-09-28 09:10:32 +0200171#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200172 int rc;
173 struct serial_struct ser_info;
174
175 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
176 if (rc < 0) {
177 dbg_perror("ioctl(TIOCGSERIAL)");
178 return -errno;
179 }
180
181 ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
182 ser_info.custom_divisor = ser_info.baud_base / baudrate;
183
184 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
185 if (rc < 0) {
186 dbg_perror("ioctl(TIOCSSERIAL)");
187 return -errno;
188 }
189
190 return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */
Sylvain Munaut96311842011-09-28 09:10:32 +0200191#elif defined(__APPLE__)
192#ifndef IOSSIOSPEED
193#define IOSSIOSPEED _IOW('T', 2, speed_t)
194#endif
195 int rc;
196
197 unsigned int speed = baudrate;
198 rc = ioctl(fd, IOSSIOSPEED, &speed);
199 if (rc < 0) {
Sylvain Munaute40549a2011-09-28 10:55:22 +0200200 dbg_perror("ioctl(IOSSIOSPEED)");
Sylvain Munaut96311842011-09-28 09:10:32 +0200201 return -errno;
202 }
203 return 0;
204#else
Pau Espin Pedrolea809df2017-06-23 11:03:55 +0200205#pragma message ("osmo_serial_set_custom_baudrate: unsupported platform")
Sylvain Munaut96311842011-09-28 09:10:32 +0200206 return 0;
207#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Clear any custom baudrate
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200211 * \param[in] fd File descriptor of the open device
212 * \returns 0 for success or negative errno.
213 *
214 * This function might not work on all OS or with all type of serial adapters
215 */
216int
217osmo_serial_clear_custom_baudrate(int fd)
218{
Sylvain Munaut96311842011-09-28 09:10:32 +0200219#ifdef __linux__
Sylvain Munaut31d3de92011-11-20 08:55:11 +0100220 int rc;
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200221 struct serial_struct ser_info;
222
223 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
224 if (rc < 0) {
225 dbg_perror("ioctl(TIOCGSERIAL)");
226 return -errno;
227 }
228
229 ser_info.flags = ASYNC_LOW_LATENCY;
230 ser_info.custom_divisor = 0;
231
232 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
233 if (rc < 0) {
234 dbg_perror("ioctl(TIOCSSERIAL)");
235 return -errno;
236 }
Sylvain Munaut96311842011-09-28 09:10:32 +0200237#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200238 return 0;
239}
240
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200241/*! @} */