-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathserialtty.c
198 lines (171 loc) · 4.29 KB
/
serialtty.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*
* nrfdfu - Nordic DFU Upgrade Utility
*
* Copyright (C) 2019 Bruno Randolf (br1@einfach.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include "log.h"
#include "serialtty.h"
#define MAX_CONF_LEN 200
static struct termios tty;
static struct termios otty;
static void serial_set_tty_speed(int baud)
{
// clang-format off
switch (baud) {
case 57600: tty.c_cflag |= B57600; break;
case 115200: tty.c_cflag |= B115200; break;
case 230400: tty.c_cflag |= B230400; break;
#ifndef __APPLE__
case 460800: tty.c_cflag |= B460800; break;
case 500000: tty.c_cflag |= B500000; break;
case 576000: tty.c_cflag |= B576000; break;
case 921600: tty.c_cflag |= B921600; break;
case 1000000: tty.c_cflag |= B1000000; break;
#endif
default: LOG_ERR("Unknown baudrate %d", baud);
}
// clang-format on
}
int serial_init(const char* dev, int baud)
{
int fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) {
LOG_ERR("Couldn't open serial device '%s'", dev);
return -1;
}
/* set necessary serial port attributes */
memset(&tty, 0, sizeof tty);
memset(&otty, 0, sizeof tty);
if (tcgetattr(fd, &otty) != 0) {
LOG_ERR("Couldn't get termio attrs");
close(fd);
return -1;
}
if (tcgetattr(fd, &tty) != 0) {
LOG_ERR("Couldn't get termio attrs");
close(fd);
return -1;
}
tty.c_iflag = IGNPAR;
tty.c_oflag = 0;
tty.c_cflag = CLOCAL | CREAD | CS8;
tty.c_lflag = 0;
serial_set_tty_speed(baud);
tcflush(fd, TCIFLUSH);
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
LOG_ERR("Couldn't set termio attrs");
close(fd);
return -1;
}
return fd;
}
void serial_fini(int sock)
{
if (sock < 0) {
return;
}
/* unset DTR */
int serialLines;
ioctl(sock, TIOCMGET, &serialLines);
serialLines &= ~TIOCM_DTR;
ioctl(sock, TIOCMSET, &serialLines);
/* reset terminal settings to original */
if (tcsetattr(sock, TCSANOW, &otty) != 0) {
LOG_ERR("Couldn't reset termio attrs");
}
close(sock);
}
bool serial_wait_read_ready(int fd, int sec)
{
if (fd < 0) {
return false;
}
struct timeval tv = {};
fd_set fds = {};
tv.tv_sec = sec;
FD_ZERO(&fds);
FD_SET(fd, &fds);
int ret = select(fd + 1, &fds, NULL, NULL, &tv);
return ret <= 0; // error or timeout
}
bool serial_wait_write_ready(int fd, int sec)
{
if (fd < 0) {
return false;
}
struct timeval tv = {};
fd_set fds = {};
tv.tv_sec = sec;
FD_ZERO(&fds);
FD_SET(fd, &fds);
int ret = select(fd + 1, NULL, &fds, NULL, &tv);
return ret <= 0; // error or timeout
}
/* write to serial handling blocking case */
bool serial_write(int fd, const char* buf, size_t len, int timeout_sec)
{
ssize_t ret;
size_t pos = 0;
if (fd < 0) {
return false;
}
do {
ret = write(fd, buf + pos, len - pos);
if (ret == -1) {
if (errno == EAGAIN) {
/* write would block, wait until ready again */
serial_wait_write_ready(fd, timeout_sec);
continue;
} else {
/* grave error */
LOG_ERR("ERR: write error: %d %s", errno, strerror(errno));
return false;
}
} else if ((size_t)ret < len - pos) {
/* partial writes usually mean next write would return
* EAGAIN, so just wait until it's ready again */
serial_wait_write_ready(fd, timeout_sec);
}
pos += ret;
} while (pos < len);
return true;
}
bool serial_set_baudrate(int fd, int baud)
{
if (fd < 0) {
return false;
}
tty.c_cflag = CLOCAL | CREAD | CS8;
serial_set_tty_speed(baud);
if (tcsetattr(fd, TCSAFLUSH, &tty) != 0) {
LOG_ERR("Couldn't set termio attrs baudrate");
return false;
}
return true;
}