w11 - cpp 0.794
Backend server for Rlink and w11
Loading...
Searching...
No Matches
RlinkPortTerm.cpp
Go to the documentation of this file.
1// $Id: RlinkPortTerm.cpp 1186 2019-07-12 17:49:59Z mueller $
2// SPDX-License-Identifier: GPL-3.0-or-later
3// Copyright 2011-2018 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
4//
5// Revision History:
6// Date Rev Version Comment
7// 2018-12-22 1091 1.3.5 Open(): add time_t cast (-Wfloat-conversion fix)
8// 2018-11-30 1075 1.3.4 use list-init
9// 2018-09-21 1048 1.3.3 coverity fixup (uninitialized field)
10// 2017-04-15 875 1.3.2 Open(): set default scheme
11// 2017-04-07 868 1.3.1 Dump(): add detail arg
12// 2015-04-12 666 1.3 drop xon/xoff excaping; add noinit attribute
13// 2015-02-01 641 1.2 support custom baud rates (5M,6M,10M,12M)
14// 2013-02-23 492 1.1 use RparseUrl
15// 2011-12-18 440 1.0.4 add kStatNPort stats; Open(): autoadd /dev/tty,
16// BUGFIX: Open(): set VSTART, VSTOP
17// 2011-12-11 438 1.0.3 Read(),Write(): added for xon handling, tcdrain();
18// Open(): add more baud rates, support xon attribute
19// 2011-12-04 435 1.0.2 Open(): add cts attr, hw flow control now optional
20// 2011-07-04 388 1.0.1 add termios readback and verification
21// 2011-03-27 374 1.0 Initial version
22// ---------------------------------------------------------------------------
23
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <unistd.h>
33#include <termios.h>
34#include <sys/ioctl.h>
35#include <linux/serial.h>
36
37#include "RlinkPortTerm.hpp"
38
39#include "librtools/RosFill.hpp"
41
42using namespace std;
43
49// all method definitions in namespace Retro
50namespace Retro {
51
52//------------------------------------------+-----------------------------------
53// constants definitions
54const uint8_t RlinkPortTerm::kc_xon;
55const uint8_t RlinkPortTerm::kc_xoff;
56
57//------------------------------------------+-----------------------------------
59
61 : RlinkPort(),
62 fTiosOld{},
63 fTiosNew{}
64{}
65
66//------------------------------------------+-----------------------------------
68
70{
72}
73
74//------------------------------------------+-----------------------------------
76
77bool RlinkPortTerm::Open(const std::string& url, RerrMsg& emsg)
78{
79 Close();
80
81 if (!fUrl.Set(url, "|baud=|break|cts|xon|noinit|", "term", emsg)) return false;
82
83 // if path doesn't start with a '/' prepend a '/dev/tty'
84 if (fUrl.Path().substr(0,1) != "/") {
85 fUrl.SetPath(string("/dev/tty" + fUrl.Path()));
86 }
87
88 speed_t speed = B115200;
89 unsigned long nsbaud = 0;
90 string baud;
91 if (fUrl.FindOpt("baud", baud)) {
92 speed = B0;
93 if (baud=="2400") speed = B2400;
94 if (baud=="4800") speed = B4800;
95 if (baud=="9600") speed = B9600;
96 if (baud=="19200" || baud=="19k") speed = B19200;
97 if (baud=="38400" || baud=="38k") speed = B38400;
98 if (baud=="57600" || baud=="57k") speed = B57600;
99 if (baud=="115200" || baud=="115k") speed = B115200;
100 if (baud=="230400" || baud=="230k") speed = B230400;
101 if (baud=="460800" || baud=="460k") speed = B460800;
102 if (baud=="500000" || baud=="500k") speed = B500000;
103 if (baud=="921600" || baud=="921k") speed = B921600;
104 if (baud=="1000000" || baud=="1000k" || baud=="1M") speed = B1000000;
105 if (baud=="1152000" || baud=="1152k") speed = B1152000;
106 if (baud=="1500000" || baud=="1500k") speed = B1500000;
107 if (baud=="2000000" || baud=="2000k" || baud=="2M") speed = B2000000;
108 if (baud=="2500000" || baud=="2500k") speed = B2500000;
109 if (baud=="3000000" || baud=="3000k" || baud=="3M") speed = B3000000;
110 if (baud=="3500000" || baud=="3500k") speed = B3500000;
111 if (baud=="4000000" || baud=="4000k" || baud=="4M") speed = B4000000;
112
113 // now handle non-standart baud rates
114 if (speed == B0) {
115 if (baud== "5000000" || baud== "5000k" || baud== "5M") nsbaud = 5000000;
116 if (baud== "6000000" || baud== "6000k" || baud== "6M") nsbaud = 6000000;
117 if (baud== "6666666" || baud== "6666k") nsbaud = 6666666;
118 if (baud=="10000000" || baud=="10000k" || baud=="10M") nsbaud = 10000000;
119 if (baud=="12000000" || baud=="12000k" || baud=="12M") nsbaud = 12000000;
120 if (nsbaud == 0) {
121 emsg.Init("RlinkPortTerm::Open()",
122 string("invalid baud rate '") + baud + "' specified");
123 return false;
124 }
125 }
126 }
127
128 int fd;
129
130 fd = open(fUrl.Path().c_str(), O_RDWR|O_NOCTTY);
131 if (fd < 0) {
132 emsg.InitErrno("RlinkPortTerm::Open()",
133 string("open() for '") + fUrl.Path() + "' failed: ",
134 errno);
135 return false;
136 }
137
138 if (!isatty(fd)) {
139 emsg.Init("RlinkPortTerm::Open()",
140 string("isatty() check for '") + fUrl.Path() +
141 "' failed: not a TTY");
142 ::close(fd);
143 return false;
144 }
145
146 if (::tcgetattr(fd, &fTiosOld) != 0) {
147 emsg.InitErrno("RlinkPortTerm::Open()",
148 string("tcgetattr() for '") + fUrl.Path() + "' failed: ",
149 errno);
150 ::close(fd);
151 return false;
152 }
153
154 struct serial_struct sioctl = {};
155 int cdivisor = 0;
156
157 if (nsbaud != 0) {
158 if (::ioctl(fd, TIOCGSERIAL, &sioctl) < 0) {
159 emsg.InitErrno("RlinkPortTerm::Open()",
160 string("ioctl(TIOCGSERIAL) for '")+fUrl.Path()+"' failed: ",
161 errno);
162 ::close(fd);
163 return false;
164 }
165 double fcdivisor = double(sioctl.baud_base) / double(nsbaud);
166 cdivisor = int(fcdivisor + 0.5);
167 speed = B38400;
168 }
169
170 bool use_cts = fUrl.FindOpt("cts");
171 bool use_xon = fUrl.FindOpt("xon");
172 fXon = use_xon;
173
175
176 fTiosNew.c_iflag = IGNBRK | // ignore breaks on input
177 IGNPAR; // ignore parity errors
178 if (use_xon) {
179 fTiosNew.c_iflag |= IXON| // XON/XOFF flow control output
180 IXOFF; // XON/XOFF flow control input
181 }
182
183 fTiosNew.c_oflag = 0;
184
185 fTiosNew.c_cflag = CS8 | // 8 bit chars
186 CSTOPB | // 2 stop bits
187 CREAD | // enable receiver
188 CLOCAL; // ignore modem control
189 if (use_cts) {
190 fTiosNew.c_cflag |= CRTSCTS; // enable hardware flow control
191 }
192
193 fTiosNew.c_lflag = 0;
194
195 if (::cfsetspeed(&fTiosNew, speed) != 0) {
196 emsg.InitErrno("RlinkPortTerm::Open()",
197 string("cfsetspeed() for '") + baud + "' failed: ",
198 errno);
199 ::close(fd);
200 return false;
201 }
202
203 if (cdivisor != 0) {
204 sioctl.flags |= ASYNC_SPD_CUST;
205 sioctl.custom_divisor = cdivisor;
206 if (::ioctl(fd, TIOCSSERIAL, &sioctl) < 0) {
207 emsg.InitErrno("RlinkPortTerm::Open()",
208 string("ioctl(TIOCSSERIAL) for '")+fUrl.Path()+"' failed: ",
209 errno);
210 ::close(fd);
211 return false;
212 }
213 }
214
215 fTiosNew.c_cc[VEOF] = 0; // undef
216 fTiosNew.c_cc[VEOL] = 0; // undef
217 fTiosNew.c_cc[VERASE] = 0; // undef
218 fTiosNew.c_cc[VINTR] = 0; // undef
219 fTiosNew.c_cc[VKILL] = 0; // undef
220 fTiosNew.c_cc[VQUIT] = 0; // undef
221 fTiosNew.c_cc[VSUSP] = 0; // undef
222 fTiosNew.c_cc[VSTART] = 0; // undef
223 fTiosNew.c_cc[VSTOP] = 0; // undef
224 fTiosNew.c_cc[VMIN] = 1; // wait for 1 char
225 fTiosNew.c_cc[VTIME] = 0; //
226 if (use_xon) {
227 fTiosNew.c_cc[VSTART] = kc_xon; // setup XON -> ^Q
228 fTiosNew.c_cc[VSTOP] = kc_xoff; // setup XOFF -> ^S
229 }
230
231 if (::tcsetattr(fd, TCSANOW, &fTiosNew) != 0) {
232 emsg.InitErrno("RlinkPortTerm::Open()",
233 string("tcsetattr() for '") + fUrl.Path() + "' failed: ",
234 errno);
235 ::close(fd);
236 return false;
237 }
238
239 // tcsetattr() returns success if any of the requested changes could be
240 // successfully carried out. Therefore the termios structure is read back
241 // and verified.
242
243 struct termios tios;
244 if (::tcgetattr(fd, &tios) != 0) {
245 emsg.InitErrno("RlinkPortTerm::Open()",
246 string("2nd tcgetattr() for '") + fUrl.Path() +
247 "' failed: ", errno);
248 ::close(fd);
249 return false;
250 }
251
252 const char* pmsg = 0;
253 if (tios.c_iflag != fTiosNew.c_iflag) pmsg = "c_iflag";
254 if (tios.c_oflag != fTiosNew.c_oflag) pmsg = "c_oflag";
255 if (tios.c_cflag != fTiosNew.c_cflag) pmsg = "c_cflag";
256 if (tios.c_lflag != fTiosNew.c_lflag) pmsg = "c_lflag";
257 if (::cfgetispeed(&tios) != speed) pmsg = "ispeed";
258 if (::cfgetospeed(&tios) != speed) pmsg = "ospeed";
259 for (int i=0; i<NCCS; i++) {
260 if (tios.c_cc[i] != fTiosNew.c_cc[i]) pmsg = "c_cc char";
261 }
262
263 // FIXME_code: why does readback fail for 38400 ?
264 if (speed != B38400 && pmsg) {
265 emsg.Init("RlinkPortTerm::Open()",
266 string("tcsetattr() failed to set") + string(pmsg));
267 ::close(fd);
268 return false;
269 }
270
271 fFdWrite = fd;
272 fFdRead = fd;
273 fIsOpen = true;
274
275 if (fUrl.FindOpt("break")) {
276 if (tcsendbreak(fd, 0) != 0) {
277 emsg.InitErrno("RlinkPortTerm::Open()",
278 string("tcsendbreak() for '") + fUrl.Path() +
279 "' failed: ", errno);
280 Close();
281 return false;
282 }
283 uint8_t buf[1];
284 buf[0] = 0x80;
285 if (Write(buf, 1, emsg) != 1) {
286 Close();
287 return false;
288 }
289 }
290
291 return true;
292}
293
294//------------------------------------------+-----------------------------------
296
298{
299 if (!IsOpen()) return;
300
301 if (fFdWrite >= 0) {
302 ::tcflush(fFdWrite, TCIOFLUSH);
303 ::tcsetattr(fFdWrite, TCSANOW, &fTiosOld);
304 }
306
307 return;
308}
309
310//------------------------------------------+-----------------------------------
312
313void RlinkPortTerm::Dump(std::ostream& os, int ind, const char* text,
314 int detail) const
315{
316 RosFill bl(ind);
317 os << bl << (text?text:"--") << "RlinkPortTerm @ " << this << endl;
318 DumpTios(os, ind, "fTiosOld", fTiosOld);
319 DumpTios(os, ind, "fTiosNew", fTiosNew);
320 RlinkPort::Dump(os, ind, " ^", detail);
321 return;
322}
323
324//------------------------------------------+-----------------------------------
326
327void RlinkPortTerm::DumpTios(std::ostream& os, int ind, const std::string& name,
328 const struct termios& tios) const
329{
330 RosFill bl(ind+2);
331 os << bl << name << ":" << endl;
332 os << bl << " c_iflag : " << RosPrintf(tios.c_iflag,"x0",8);
333 if (tios.c_iflag & BRKINT) os << " BRKINT";
334 if (tios.c_iflag & ICRNL) os << " ICRNL ";
335 if (tios.c_iflag & IGNBRK) os << " IGNBRK";
336 if (tios.c_iflag & IGNCR) os << " IGNCR ";
337 if (tios.c_iflag & IGNPAR) os << " IGNPAR";
338 if (tios.c_iflag & INLCR) os << " INLCR ";
339 if (tios.c_iflag & INPCK) os << " INPCK ";
340 if (tios.c_iflag & ISTRIP) os << " ISTRIP";
341 if (tios.c_iflag & IXOFF) os << " IXOFF ";
342 if (tios.c_iflag & IXON) os << " IXON ";
343 if (tios.c_iflag & PARMRK) os << " PARMRK";
344 os << endl;
345
346 os << bl << " c_oflag : " << RosPrintf(tios.c_oflag,"x0",8);
347 if (tios.c_oflag & OPOST) os << " OPOST ";
348 os << endl;
349
350 os << bl << " c_cflag : " << RosPrintf(tios.c_cflag,"x0",8);
351 if (tios.c_cflag & CLOCAL) os << " CLOCAL";
352 if (tios.c_cflag & CREAD) os << " CREAD ";
353 if ((tios.c_cflag & CSIZE) == CS5) os << " CS5 ";
354 if ((tios.c_cflag & CSIZE) == CS6) os << " CS6 ";
355 if ((tios.c_cflag & CSIZE) == CS7) os << " CS7 ";
356 if ((tios.c_cflag & CSIZE) == CS8) os << " CS8 ";
357 if (tios.c_cflag & CSTOPB) os << " CSTOPB";
358 if (tios.c_cflag & HUPCL) os << " HUPCL ";
359 if (tios.c_cflag & PARENB) os << " PARENB";
360 if (tios.c_cflag & PARODD) os << " PARODD";
361 speed_t speed = cfgetispeed(&tios);
362 int baud = 0;
363 if (speed == B2400) baud = 2400;
364 if (speed == B4800) baud = 4800;
365 if (speed == B9600) baud = 9600;
366 if (speed == B19200) baud = 19200;
367 if (speed == B38400) baud = 38400;
368 if (speed == B57600) baud = 57600;
369 if (speed == B115200) baud = 115200;
370 if (speed == B230400) baud = 230400;
371 if (speed == B460800) baud = 460800;
372 if (speed == B500000) baud = 500000;
373 if (speed == B921600) baud = 921600;
374 if (speed == B1000000) baud = 1000000;
375 if (speed == B1152000) baud = 1152000;
376 if (speed == B1500000) baud = 1500000;
377 if (speed == B2000000) baud = 2000000;
378 if (speed == B2500000) baud = 2500000;
379 if (speed == B3000000) baud = 3000000;
380 if (speed == B3500000) baud = 3500000;
381 if (speed == B4000000) baud = 4000000;
382 os << " speed: " << RosPrintf(baud, "d", 7);
383 os << endl;
384
385 os << bl << " c_lflag : " << RosPrintf(tios.c_lflag,"x0",8);
386 if (tios.c_lflag & ECHO) os << " ECHO ";
387 if (tios.c_lflag & ECHOE) os << " ECHOE ";
388 if (tios.c_lflag & ECHOK) os << " ECHOK ";
389 if (tios.c_lflag & ECHONL) os << " ECHONL";
390 if (tios.c_lflag & ICANON) os << " ICANON";
391 if (tios.c_lflag & IEXTEN) os << " IEXTEN";
392 if (tios.c_lflag & ISIG) os << " ISIG ";
393 if (tios.c_lflag & NOFLSH) os << " NOFLSH";
394 if (tios.c_lflag & TOSTOP) os << " TOSTOP";
395 os << endl;
396
397 os << bl << " c_cc : " << endl;
398 os << bl << " [VEOF] : " << RosPrintf(tios.c_cc[VEOF],"o",3);
399 os << " [VEOL] : " << RosPrintf(tios.c_cc[VEOL],"o",3);
400 os << " [VERASE]: " << RosPrintf(tios.c_cc[VERASE],"o",3);
401 os << " [VINTR] : " << RosPrintf(tios.c_cc[VINTR],"o",3) << endl;
402 os << bl << " [VKILL] : " << RosPrintf(tios.c_cc[VKILL],"o",3);
403 os << " [VQUIT] : " << RosPrintf(tios.c_cc[VQUIT],"o",3);
404 os << " [VSUSP] : " << RosPrintf(tios.c_cc[VSUSP],"o",3);
405 os << " [VSTART]: " << RosPrintf(tios.c_cc[VSTART],"o",3) << endl;
406 os << bl << " [VSTOP] : " << RosPrintf(tios.c_cc[VSTOP],"o",3);
407 os << " [VMIN] : " << RosPrintf(tios.c_cc[VMIN],"o",3);
408 os << " [VTIME] : " << RosPrintf(tios.c_cc[VTIME],"o",3) << endl;
409
410 return;
411}
412
413} // end namespace Retro
FIXME_docs.
Definition: RerrMsg.hpp:25
void Init(const std::string &meth, const std::string &text)
FIXME_docs.
Definition: RerrMsg.cpp:74
void InitErrno(const std::string &meth, const std::string &text, int errnum)
FIXME_docs.
Definition: RerrMsg.cpp:84
virtual ~RlinkPortTerm()
Destructor.
virtual bool Open(const std::string &url, RerrMsg &emsg)
FIXME_docs.
struct termios fTiosOld
virtual void Close()
FIXME_docs.
virtual void Dump(std::ostream &os, int ind=0, const char *text=0, int detail=0) const
FIXME_docs.
RlinkPortTerm()
Default constructor.
struct termios fTiosNew
void DumpTios(std::ostream &os, int ind, const std::string &name, const struct termios &tios) const
FIXME_docs.
static const uint8_t kc_xon
static const uint8_t kc_xoff
FIXME_docs.
Definition: RlinkPort.hpp:45
int fFdWrite
fd for write
Definition: RlinkPort.hpp:109
bool IsOpen() const
FIXME_docs.
Definition: RlinkPort.ipp:27
RparseUrl fUrl
parsed url
Definition: RlinkPort.hpp:106
int fFdRead
fd for read
Definition: RlinkPort.hpp:108
bool fIsOpen
is open flag
Definition: RlinkPort.hpp:105
bool fXon
xon attribute set
Definition: RlinkPort.hpp:107
virtual void Close()
FIXME_docs.
Definition: RlinkPort.cpp:98
virtual int Write(const uint8_t *buf, size_t size, RerrMsg &emsg)
FIXME_docs.
Definition: RlinkPort.cpp:166
virtual void Dump(std::ostream &os, int ind=0, const char *text=0, int detail=0) const
FIXME_docs.
Definition: RlinkPort.cpp:288
I/O appicator to generate fill characters.
Definition: RosFill.hpp:24
bool FindOpt(const std::string &name) const
FIXME_docs.
Definition: RparseUrl.cpp:196
bool Set(const std::string &url, const std::string &optlist, RerrMsg &emsg)
FIXME_docs.
Definition: RparseUrl.cpp:55
void SetPath(const std::string &path)
FIXME_docs.
Definition: RparseUrl.ipp:20
const std::string & Path() const
FIXME_docs.
Definition: RparseUrl.ipp:45
RosPrintfS< bool > RosPrintf(bool value, const char *form=0, int width=0, int prec=0)
Creates a print object for the formatted output of a bool value.
Definition: RosPrintf.ipp:38
Declaration of class ReventLoop.
Definition: ReventLoop.cpp:47