blob: e61edb71cb6f6dcee1f5b5f51fbf1d4d47e9e6da [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
30 * \file Osmocom serial port helpers
31 */
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{
62 int rc, fd=0, v24;
63 struct termios tio;
64
65 /* Open device */
66 fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
67 if (fd < 0) {
68 dbg_perror("open");
69 return -errno;
70 }
71
72 /* Configure serial interface */
73 rc = tcgetattr(fd, &tio);
74 if (rc < 0) {
75 dbg_perror("tcgetattr()");
76 rc = -errno;
77 goto error;
78 }
79
80 cfsetispeed(&tio, baudrate);
81 cfsetospeed(&tio, baudrate);
82
83 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
84 tio.c_cflag |= (CREAD | CLOCAL | CS8);
85 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
86 tio.c_iflag |= (INPCK | ISTRIP);
87 tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
88 tio.c_oflag &= ~(OPOST | ONLCR);
89
90 rc = tcsetattr(fd, TCSANOW, &tio);
91 if (rc < 0) {
92 dbg_perror("tcsetattr()");
93 rc = -errno;
94 goto error;
95 }
96
97 /* Set ready to read/write */
98 v24 = TIOCM_DTR | TIOCM_RTS;
99 rc = ioctl(fd, TIOCMBIS, &v24);
100 if (rc < 0) {
101 dbg_perror("ioctl(TIOCMBIS)");
102 rc = -errno;
103 goto error;
104 }
105
106 return fd;
107
108error:
109 if (fd)
110 close(fd);
111 return rc;
112}
113
114static int
115_osmo_serial_set_baudrate(int fd, speed_t baudrate)
116{
117 int rc;
118 struct termios tio;
119
120 rc = tcgetattr(fd, &tio);
121 if (rc < 0) {
122 dbg_perror("tcgetattr()");
123 return -errno;
124 }
125 cfsetispeed(&tio, baudrate);
126 cfsetospeed(&tio, baudrate);
127
128 rc = tcsetattr(fd, TCSANOW, &tio);
129 if (rc < 0) {
130 dbg_perror("tcgetattr()");
131 return -errno;
132 }
133
134 return 0;
135}
136
137/*! \brief Change current baudrate
138 * \param[in] fd File descriptor of the open device
139 * \param[in] baudrate Baudrate constant (speed_t: B9600, B...)
140 * \returns 0 for success or negative errno.
141 */
142int
143osmo_serial_set_baudrate(int fd, speed_t baudrate)
144{
145 osmo_serial_clear_custom_baudrate(fd);
146 return _osmo_serial_set_baudrate(fd, baudrate);
147}
148
149/*! \brief Change current baudrate to a custom one using OS specific method
150 * \param[in] fd File descriptor of the open device
151 * \param[in] baudrate Baudrate as integer
152 * \returns 0 for success or negative errno.
153 *
154 * This function might not work on all OS or with all type of serial adapters
155 */
156int
157osmo_serial_set_custom_baudrate(int fd, int baudrate)
158{
Sylvain Munaut96311842011-09-28 09:10:32 +0200159#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200160 int rc;
161 struct serial_struct ser_info;
162
163 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
164 if (rc < 0) {
165 dbg_perror("ioctl(TIOCGSERIAL)");
166 return -errno;
167 }
168
169 ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
170 ser_info.custom_divisor = ser_info.baud_base / baudrate;
171
172 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
173 if (rc < 0) {
174 dbg_perror("ioctl(TIOCSSERIAL)");
175 return -errno;
176 }
177
178 return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */
Sylvain Munaut96311842011-09-28 09:10:32 +0200179#elif defined(__APPLE__)
180#ifndef IOSSIOSPEED
181#define IOSSIOSPEED _IOW('T', 2, speed_t)
182#endif
183 int rc;
184
185 unsigned int speed = baudrate;
186 rc = ioctl(fd, IOSSIOSPEED, &speed);
187 if (rc < 0) {
Sylvain Munaute40549a2011-09-28 10:55:22 +0200188 dbg_perror("ioctl(IOSSIOSPEED)");
Sylvain Munaut96311842011-09-28 09:10:32 +0200189 return -errno;
190 }
191 return 0;
192#else
193#warning osmo_serial_set_custom_baudrate: unsupported platform
194 return 0;
195#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200196}
197
198/*! \brief Clear any custom baudrate
199 * \param[in] fd File descriptor of the open device
200 * \returns 0 for success or negative errno.
201 *
202 * This function might not work on all OS or with all type of serial adapters
203 */
204int
205osmo_serial_clear_custom_baudrate(int fd)
206{
207 int rc;
Sylvain Munaut96311842011-09-28 09:10:32 +0200208#ifdef __linux__
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200209 struct serial_struct ser_info;
210
211 rc = ioctl(fd, TIOCGSERIAL, &ser_info);
212 if (rc < 0) {
213 dbg_perror("ioctl(TIOCGSERIAL)");
214 return -errno;
215 }
216
217 ser_info.flags = ASYNC_LOW_LATENCY;
218 ser_info.custom_divisor = 0;
219
220 rc = ioctl(fd, TIOCSSERIAL, &ser_info);
221 if (rc < 0) {
222 dbg_perror("ioctl(TIOCSSERIAL)");
223 return -errno;
224 }
Sylvain Munaut96311842011-09-28 09:10:32 +0200225#endif
Sylvain Munautfe28ded2011-09-02 22:18:24 +0200226 return 0;
227}
228
229/*! }@ */