blob: 6316e45970a21882e6cf7d97ed5263019203ef55 [file] [log] [blame]
Sylvain Munautfe28ded2011-09-02 22:18:24 +02001/*
2 * serial.c
3 *
4 * Utility functions to deal with serial ports
5 *
6 * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
7 *
8 * All Rights Reserved
9 *
10 * 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 * @{
27 */
28
29/*! \file serial.c
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +010030 * Osmocom serial port helpers
Sylvain Munautfe28ded2011-09-02 22:18:24 +020031 */
32
33#include <errno.h>
34#include <fcntl.h>
35#include <stdio.h>
36#include <termios.h>
37#include <unistd.h>
38#include <sys/ioctl.h>
39#include <sys/types.h>
40#include <sys/stat.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020041#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +020042#include <linux/serial.h>
Sylvain Munaut96311842011-09-28 09:10:32 +020043#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +020044
45#include <osmocom/core/serial.h>
46
47
48#if 0
49# define dbg_perror(x) perror(x)
50#else
51# define dbg_perror(x) do { } while (0)
52#endif
53
54/*! \brief Open serial device and does base init
55 * \param[in] dev Path to the device node to open
56 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
57 * \returns >=0 file descriptor in case of success or negative errno.
58 */
59int
60osmo_serial_init(const char *dev, speed_t baudrate)
61{
Harald Welte1db37822016-12-23 22:49:39 +010062 int rc, fd=0, v24, flags;
Sylvain Munautfe28ded2011-09-02 22:18:24 +020063 struct termios tio;
64
Harald Welte1db37822016-12-23 22:49:39 +010065 /* Use nonblock as the device might block otherwise */
66 fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
Sylvain Munautfe28ded2011-09-02 22:18:24 +020067 if (fd < 0) {
68 dbg_perror("open");
69 return -errno;
70 }
71
Harald Welte1db37822016-12-23 22:49:39 +010072 /* now put it into blcoking mode */
73 flags = fcntl(fd, F_GETFL, 0);
74 if (flags < 0) {
75 dbg_perror("fcntl get flags");
Harald Welted2510452016-12-24 17:58:13 +010076 rc = -errno;
77 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010078 }
79
80 flags &= ~O_NONBLOCK;
81 rc = fcntl(fd, F_SETFL, flags);
82 if (rc != 0) {
83 dbg_perror("fcntl set flags");
Harald Welted2510452016-12-24 17:58:13 +010084 rc = -errno;
85 goto error;
Harald Welte1db37822016-12-23 22:49:39 +010086 }
87
Sylvain Munautfe28ded2011-09-02 22:18:24 +020088 /* Configure serial interface */
89 rc = tcgetattr(fd, &tio);
90 if (rc < 0) {
91 dbg_perror("tcgetattr()");
92 rc = -errno;
93 goto error;
94 }
95
96 cfsetispeed(&tio, baudrate);
97 cfsetospeed(&tio, baudrate);
98
99 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
100 tio.c_cflag |= (CREAD | CLOCAL | CS8);
101 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
102 tio.c_iflag |= (INPCK | ISTRIP);
103 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:
125 if (fd)
126 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 }
141 cfsetispeed(&tio, baudrate);
142 cfsetospeed(&tio, baudrate);
143
144 rc = tcsetattr(fd, TCSANOW, &tio);
145 if (rc < 0) {
146 dbg_perror("tcgetattr()");
147 return -errno;
148 }
149
150 return 0;
151}
152
153/*! \brief Change current baudrate
154 * \param[in] fd File descriptor of the open device
155 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
156 * \returns 0 for success or negative errno.
157 */
158int
159osmo_serial_set_baudrate(int fd, speed_t baudrate)
160{
161 osmo_serial_clear_custom_baudrate(fd);
162 return _osmo_serial_set_baudrate(fd, baudrate);
163}
164
165/*! \brief Change current baudrate to a custom one using OS specific method
166 * \param[in] fd File descriptor of the open device
167 * \param[in] baudrate Baudrate as integer
168 * \returns 0 for success or negative errno.
169 *
170 * This function might not work on all OS or with all type of serial adapters
171 */
172int
173osmo_serial_set_custom_baudrate(int fd, int baudrate)
174{
Sylvain Munaut96311842011-09-28 09:10:32 +0200175#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200176 int rc;
177 struct serial_struct ser_info;
178
179 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
180 if (rc < 0) {
181 dbg_perror("ioctl(TIOCGSERIAL)");
182 return -errno;
183 }
184
185 ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
186 ser_info.custom_divisor = ser_info.baud_base / baudrate;
187
188 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
189 if (rc < 0) {
190 dbg_perror("ioctl(TIOCSSERIAL)");
191 return -errno;
192 }
193
194 return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */
Sylvain Munaut96311842011-09-28 09:10:32 +0200195#elif defined(__APPLE__)
196#ifndef IOSSIOSPEED
197#define IOSSIOSPEED _IOW('T', 2, speed_t)
198#endif
199 int rc;
200
201 unsigned int speed = baudrate;
202 rc = ioctl(fd, IOSSIOSPEED, &speed);
203 if (rc < 0) {
Sylvain Munaute40549a2011-09-28 10:55:22 +0200204 dbg_perror("ioctl(IOSSIOSPEED)");
Sylvain Munaut96311842011-09-28 09:10:32 +0200205 return -errno;
206 }
207 return 0;
208#else
209#warning osmo_serial_set_custom_baudrate: unsupported platform
210 return 0;
211#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200212}
213
214/*! \brief Clear any custom baudrate
215 * \param[in] fd File descriptor of the open device
216 * \returns 0 for success or negative errno.
217 *
218 * This function might not work on all OS or with all type of serial adapters
219 */
220int
221osmo_serial_clear_custom_baudrate(int fd)
222{
Sylvain Munaut96311842011-09-28 09:10:32 +0200223#ifdef __linux__
Sylvain Munaut31d3de92011-11-20 08:55:11 +0100224 int rc;
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200225 struct serial_struct ser_info;
226
227 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
228 if (rc < 0) {
229 dbg_perror("ioctl(TIOCGSERIAL)");
230 return -errno;
231 }
232
233 ser_info.flags = ASYNC_LOW_LATENCY;
234 ser_info.custom_divisor = 0;
235
236 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
237 if (rc < 0) {
238 dbg_perror("ioctl(TIOCSSERIAL)");
239 return -errno;
240 }
Sylvain Munaut96311842011-09-28 09:10:32 +0200241#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200242 return 0;
243}
244
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200245/*! @} */