OpenCores
URL https://opencores.org/ocsvn/w11/w11/trunk

Subversion Repositories w11

[/] [w11/] [tags/] [w11a_V0.61/] [tools/] [src/] [librlink/] [RlinkPortTerm.cpp] - Blame information for rev 21

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 21 wfjm
// $Id: RlinkPortTerm.cpp 516 2013-05-05 21:24:52Z mueller $
2 10 wfjm
//
3 19 wfjm
// Copyright 2011-2013 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
4 10 wfjm
//
5
// This program is free software; you may redistribute and/or modify it under
6
// the terms of the GNU General Public License as published by the Free
7
// Software Foundation, either version 2, or at your option any later version.
8
//
9
// This program is distributed in the hope that it will be useful, but
10
// WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
11
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12
// for complete details.
13
// 
14
// Revision History: 
15
// Date         Rev Version  Comment
16 19 wfjm
// 2013-02-23   492   1.1    use RparseUrl
17 16 wfjm
// 2011-12-18   440   1.0.4  add kStatNPort stats; Open(): autoadd /dev/tty,
18
//                           BUGFIX: Open(): set VSTART, VSTOP
19
// 2011-12-11   438   1.0.3  Read(),Write(): added for xon handling, tcdrain();
20
//                           Open(): add more baud rates, support xon attribute
21 15 wfjm
// 2011-12-04   435   1.0.2  Open(): add cts attr, hw flow control now optional
22 12 wfjm
// 2011-07-04   388   1.0.1  add termios readback and verification
23 10 wfjm
// 2011-03-27   374   1.0    Initial version
24
// ---------------------------------------------------------------------------
25
 
26
/*!
27
  \file
28 21 wfjm
  \version $Id: RlinkPortTerm.cpp 516 2013-05-05 21:24:52Z mueller $
29 10 wfjm
  \brief   Implemenation of RlinkPortTerm.
30
*/
31
 
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
#include <fcntl.h>
35
#include <errno.h>
36
#include <unistd.h>
37
#include <termios.h>
38
 
39
#include "RlinkPortTerm.hpp"
40
 
41
#include "librtools/RosFill.hpp"
42
#include "librtools/RosPrintf.hpp"
43
 
44
using namespace std;
45
 
46
/*!
47
  \class Retro::RlinkPortTerm
48 19 wfjm
  \brief FIXME_docs
49 10 wfjm
*/
50
 
51 19 wfjm
// all method definitions in namespace Retro
52
namespace Retro {
53
 
54 10 wfjm
//------------------------------------------+-----------------------------------
55 16 wfjm
// constants definitions
56
const uint8_t RlinkPortTerm::kc_xon;
57
const uint8_t RlinkPortTerm::kc_xoff;
58
const uint8_t RlinkPortTerm::kc_xesc;
59
 
60
//------------------------------------------+-----------------------------------
61 10 wfjm
//! Default constructor
62
 
63
RlinkPortTerm::RlinkPortTerm()
64
  : RlinkPort()
65 16 wfjm
{
66
  fStats.Define(kStatNPortTxXesc, "NPortTxXesc", "Tx XESC escapes");
67
  fStats.Define(kStatNPortRxXesc, "NPortRxXesc", "Rx XESC escapes");
68
}
69 10 wfjm
 
70
//------------------------------------------+-----------------------------------
71
//! Destructor
72
 
73
RlinkPortTerm::~RlinkPortTerm()
74
{
75 19 wfjm
  RlinkPortTerm::Close();
76 10 wfjm
}
77
 
78
//------------------------------------------+-----------------------------------
79 19 wfjm
//! FIXME_docs
80 10 wfjm
 
81
bool RlinkPortTerm::Open(const std::string& url, RerrMsg& emsg)
82
{
83 19 wfjm
  Close();
84 10 wfjm
 
85 19 wfjm
  if (!fUrl.Set(url, "|baud=|break|cts|xon|", emsg)) return false;
86 10 wfjm
 
87 16 wfjm
  // if path doesn't start with a '/' prepend a '/dev/tty'
88 19 wfjm
  if (fUrl.Path().substr(0,1) != "/") {
89
    fUrl.SetPath(string("/dev/tty" + fUrl.Path()));
90 16 wfjm
  }
91
 
92 10 wfjm
  speed_t speed = B115200;
93
  string baud;
94 19 wfjm
  if (fUrl.FindOpt("baud", baud)) {
95 10 wfjm
    speed = B0;
96 16 wfjm
    if (baud=="2400")                     speed = B2400;
97
    if (baud=="4800")                     speed = B4800;
98 10 wfjm
    if (baud=="9600")                     speed = B9600;
99
    if (baud=="19200"   || baud=="19k")   speed = B19200;
100
    if (baud=="38400"   || baud=="38k")   speed = B38400;
101
    if (baud=="57600"   || baud=="57k")   speed = B57600;
102
    if (baud=="115200"  || baud=="115k")  speed = B115200;
103
    if (baud=="230400"  || baud=="230k")  speed = B230400;
104
    if (baud=="460800"  || baud=="460k")  speed = B460800;
105
    if (baud=="500000"  || baud=="500k")  speed = B500000;
106
    if (baud=="921600"  || baud=="921k")  speed = B921600;
107 16 wfjm
    if (baud=="1000000" || baud=="1000k" || baud=="1M") speed = B1000000;
108
    if (baud=="1152000" || baud=="1152k")               speed = B1152000;
109
    if (baud=="1500000" || baud=="1500k")               speed = B1500000;
110
    if (baud=="2000000" || baud=="2000k" || baud=="2M") speed = B2000000;
111
    if (baud=="2500000" || baud=="2500k")               speed = B2500000;
112
    if (baud=="3000000" || baud=="3000k" || baud=="3M") speed = B3000000;
113
    if (baud=="3500000" || baud=="3500k")               speed = B3500000;
114
    if (baud=="4000000" || baud=="4000k" || baud=="4M") speed = B4000000;
115 10 wfjm
    if (speed == B0) {
116
      emsg.Init("RlinkPortTerm::Open()",
117 21 wfjm
                string("invalid baud rate '") + baud + "' specified");
118 10 wfjm
      return false;
119
    }
120
  }
121
 
122
  int fd;
123
 
124 19 wfjm
  fd = open(fUrl.Path().c_str(), O_RDWR|O_NOCTTY);
125 10 wfjm
  if (fd < 0) {
126
    emsg.InitErrno("RlinkPortTerm::Open()",
127 21 wfjm
                   string("open() for '") + fUrl.Path() + "' failed: ",
128 10 wfjm
                   errno);
129
    return false;
130
  }
131
 
132
  if (!isatty(fd)) {
133
    emsg.Init("RlinkPortTerm::Open()",
134 21 wfjm
              string("isatty() check for '") + fUrl.Path() +
135
              "' failed: not a TTY");
136 10 wfjm
    close(fd);
137
    return false;
138
  }
139
 
140
  if (tcgetattr(fd, &fTiosOld) != 0) {
141
    emsg.InitErrno("RlinkPortTerm::Open()",
142 21 wfjm
                   string("tcgetattr() for '") + fUrl.Path() + "' failed: ",
143 10 wfjm
                   errno);
144
    close(fd);
145
    return false;
146
  }
147
 
148 19 wfjm
  bool use_cts = fUrl.FindOpt("cts");
149
  bool use_xon = fUrl.FindOpt("xon");
150 16 wfjm
  fUseXon = use_xon;
151
  fPendXesc = false;
152
 
153 10 wfjm
  fTiosNew = fTiosOld;
154
 
155
  fTiosNew.c_iflag = IGNBRK |               // ignore breaks on input
156
                     IGNPAR;                // ignore parity errors
157 16 wfjm
  if (use_xon) {
158
    fTiosNew.c_iflag |= IXON|               // XON/XOFF flow control output
159
                        IXOFF;              // XON/XOFF flow control input
160
  }
161 15 wfjm
 
162 10 wfjm
  fTiosNew.c_oflag = 0;
163 15 wfjm
 
164 10 wfjm
  fTiosNew.c_cflag = CS8 |                  // 8 bit chars
165
                     CSTOPB |               // 2 stop bits
166
                     CREAD |                // enable receiver
167 15 wfjm
                     CLOCAL;                // ignore modem control
168 16 wfjm
  if (use_cts) {
169 15 wfjm
    fTiosNew.c_cflag |= CRTSCTS;            // enable hardware flow control
170
  }
171
 
172 10 wfjm
  fTiosNew.c_lflag = 0;
173
 
174
  if (cfsetspeed(&fTiosNew, speed) != 0) {
175
    emsg.InitErrno("RlinkPortTerm::Open()",
176 21 wfjm
                   string("cfsetspeed() for '") + baud + "' failed: ",
177 10 wfjm
                   errno);
178
    close(fd);
179
    return false;
180
  }
181
 
182
  fTiosNew.c_cc[VEOF]   = 0;                // undef
183
  fTiosNew.c_cc[VEOL]   = 0;                // undef
184
  fTiosNew.c_cc[VERASE] = 0;                // undef
185
  fTiosNew.c_cc[VINTR]  = 0;                // undef
186
  fTiosNew.c_cc[VKILL]  = 0;                // undef
187
  fTiosNew.c_cc[VQUIT]  = 0;                // undef
188
  fTiosNew.c_cc[VSUSP]  = 0;                // undef
189
  fTiosNew.c_cc[VSTART] = 0;                // undef
190
  fTiosNew.c_cc[VSTOP]  = 0;                // undef
191
  fTiosNew.c_cc[VMIN]   = 1;                // wait for 1 char
192
  fTiosNew.c_cc[VTIME]  = 0;                // 
193 16 wfjm
  if (use_xon) {
194
    fTiosNew.c_cc[VSTART] = kc_xon;         // setup XON  -> ^Q
195
    fTiosNew.c_cc[VSTOP]  = kc_xoff;        // setup XOFF -> ^S   
196
  }
197 10 wfjm
 
198
  if (tcsetattr(fd, TCSANOW, &fTiosNew) != 0) {
199
    emsg.InitErrno("RlinkPortTerm::Open()",
200 21 wfjm
                   string("tcsetattr() for '") + fUrl.Path() + "' failed: ",
201 10 wfjm
                   errno);
202
    close(fd);
203
    return false;
204
  }
205
 
206 12 wfjm
  // tcsetattr() returns success if any of the requested changes could be
207
  // successfully carried out. Therefore the termios structure is read back
208
  // and verified.
209
 
210
  struct termios tios;
211
  if (tcgetattr(fd, &tios) != 0) {
212
    emsg.InitErrno("RlinkPortTerm::Open()",
213 21 wfjm
                   string("2nd tcgetattr() for '") + fUrl.Path() +
214
                   "' failed: ", errno);
215 12 wfjm
    close(fd);
216
    return false;
217
  }
218
 
219
  const char* pmsg = 0;
220
  if (tios.c_iflag != fTiosNew.c_iflag) pmsg = "c_iflag";
221
  if (tios.c_oflag != fTiosNew.c_oflag) pmsg = "c_oflag";
222
  if (tios.c_cflag != fTiosNew.c_cflag) pmsg = "c_cflag";
223
  if (tios.c_lflag != fTiosNew.c_lflag) pmsg = "c_lflag";
224
  if (cfgetispeed(&tios) != speed)      pmsg = "ispeed";
225
  if (cfgetospeed(&tios) != speed)      pmsg = "ospeed";
226
  for (int i=0; i<NCCS; i++) {
227
    if (tios.c_cc[i] != fTiosNew.c_cc[i]) pmsg = "c_cc char";
228
  }
229
 
230
  if (pmsg) {
231
    emsg.Init("RlinkPortTerm::Open()",
232 21 wfjm
              string("tcsetattr() failed to set") + string(pmsg));
233 12 wfjm
    close(fd);
234
    return false;
235
  }
236
 
237 10 wfjm
  fFdWrite = fd;
238
  fFdRead  = fd;
239
  fIsOpen  = true;
240
 
241 19 wfjm
  if (fUrl.FindOpt("break")) {
242 10 wfjm
    if (tcsendbreak(fd, 0) != 0) {
243
      emsg.InitErrno("RlinkPortTerm::Open()",
244 19 wfjm
                     string("tcsendbreak() for '") + fUrl.Path() +
245 21 wfjm
                     "' failed: ", errno);
246 10 wfjm
      Close();
247
      return false;
248
    }
249
    uint8_t buf[1];
250
    buf[0] = 0x80;
251
    if (Write(buf, 1, emsg) != 1) {
252
      Close();
253
      return false;
254
    }
255
  }
256
 
257
  return true;
258
}
259
 
260
//------------------------------------------+-----------------------------------
261 19 wfjm
//! FIXME_docs
262 10 wfjm
 
263
void RlinkPortTerm::Close()
264
{
265 17 wfjm
  if (!IsOpen()) return;
266
 
267
  if (fFdWrite >= 0) {
268
    tcflush(fFdWrite, TCIOFLUSH);
269
    tcsetattr(fFdWrite, TCSANOW, &fTiosOld);
270 10 wfjm
  }
271 17 wfjm
  RlinkPort::Close();
272
 
273 10 wfjm
  return;
274
}
275
 
276
//------------------------------------------+-----------------------------------
277 16 wfjm
//! FIXME_docs
278
 
279
int RlinkPortTerm::Read(uint8_t* buf, size_t size, double timeout,
280
                        RerrMsg& emsg)
281
{
282
  int irc;
283
  if (fUseXon) {
284
    uint8_t* po = buf;
285
    if (fRxBuf.size() < size) fRxBuf.resize(size);
286
 
287 17 wfjm
    // repeat read until at least one byte returned (or an error occurs)
288 16 wfjm
    // this avoids that the Read() returns with 0 in case only one byte is
289
    // seen and this is a kc_xesc. At most two iterations possible because
290
    // in 2nd iteration fPendXesc must be set and thus po pushed.
291
    while (po == buf) {
292
      irc = RlinkPort::Read(fRxBuf.data(), size, timeout, emsg);
293
      if (irc <= 0) break;
294
      uint8_t* pi = fRxBuf.data();
295
      for (int i=0; i<irc; i++) {
296
        uint8_t c = *pi++;
297
        if (fPendXesc) {
298
          *po++ = ~c;
299
          fPendXesc = false;
300
        } else if (c == kc_xesc) {
301
          fStats.Inc(kStatNPortRxXesc);
302
          fPendXesc = true;
303
        } else {
304
          *po++ = c;
305
        }
306
      }
307
      irc = po - buf;                       // set irc to # of unescaped bytes
308
    }
309
 
310
  } else {
311
    irc = RlinkPort::Read(buf, size, timeout, emsg);
312
  }
313
 
314
  return irc;
315
}
316
 
317
//------------------------------------------+-----------------------------------
318
//! FIXME_docs
319
 
320
int RlinkPortTerm::Write(const uint8_t* buf, size_t size, RerrMsg& emsg)
321
{
322
  int irc = 0;
323
 
324
  if (fUseXon) {
325
    fTxBuf.clear();
326
    const uint8_t* pc = buf;
327
 
328
    for (size_t i=0; i<size; i++) {
329
      uint8_t c = *pc++;
330
      if (c==kc_xon || c==kc_xoff || c==kc_xesc) {
331
        fStats.Inc(kStatNPortTxXesc);
332
        fTxBuf.push_back(kc_xesc);
333
        fTxBuf.push_back(~c);
334
      } else {
335
        fTxBuf.push_back(c);
336
      }
337
    }
338
    int irce = RlinkPort::Write(fTxBuf.data(), fTxBuf.size(), emsg);
339
    if (irce == (int)fTxBuf.size()) irc = size;
340
  } else {
341
    irc = RlinkPort::Write(buf, size, emsg);
342
  }
343
 
344
  /* tcdrain(fFdWrite);*/
345
  return irc;
346
}
347
 
348
//------------------------------------------+-----------------------------------
349 19 wfjm
//! FIXME_docs
350 10 wfjm
 
351
void RlinkPortTerm::Dump(std::ostream& os, int ind, const char* text) const
352
{
353
  RosFill bl(ind);
354
  os << bl << (text?text:"--") << "RlinkPortTerm @ " << this << endl;
355
  DumpTios(os, ind, "fTiosOld", fTiosOld);
356
  DumpTios(os, ind, "fTiosNew", fTiosNew);
357 19 wfjm
  RlinkPort::Dump(os, ind, " ^");
358 10 wfjm
  return;
359 19 wfjm
}
360 10 wfjm
 
361
//------------------------------------------+-----------------------------------
362 19 wfjm
//! FIXME_docs
363 10 wfjm
 
364
void RlinkPortTerm::DumpTios(std::ostream& os, int ind, const std::string& name,
365
                             const struct termios& tios) const
366
{
367
  RosFill bl(ind+2);
368
  os << bl << name << ":" << endl;
369
  os << bl << "  c_iflag : " << RosPrintf(tios.c_iflag,"x0",8);
370
  if (tios.c_iflag & BRKINT) os << " BRKINT";
371
  if (tios.c_iflag & ICRNL)  os << " ICRNL ";
372
  if (tios.c_iflag & IGNBRK) os << " IGNBRK";
373
  if (tios.c_iflag & IGNCR)  os << " IGNCR ";
374
  if (tios.c_iflag & IGNPAR) os << " IGNPAR";
375
  if (tios.c_iflag & INLCR)  os << " INLCR ";
376
  if (tios.c_iflag & INPCK)  os << " INPCK ";
377
  if (tios.c_iflag & ISTRIP) os << " ISTRIP";
378
  if (tios.c_iflag & IXOFF)  os << " IXOFF ";
379
  if (tios.c_iflag & IXON)   os << " IXON  ";
380
  if (tios.c_iflag & PARMRK) os << " PARMRK";
381
  os << endl;
382
 
383
  os << bl << "  c_oflag : " << RosPrintf(tios.c_oflag,"x0",8);
384
  if (tios.c_oflag & OPOST)  os << " OPOST ";
385
  os << endl;
386
 
387
  os << bl << "  c_cflag : " << RosPrintf(tios.c_cflag,"x0",8);
388
  if (tios.c_cflag & CLOCAL) os << " CLOCAL";
389
  if (tios.c_cflag & CREAD)  os << " CREAD ";
390
  if ((tios.c_cflag & CSIZE) == CS5)  os << " CS5   ";
391
  if ((tios.c_cflag & CSIZE) == CS6)  os << " CS6   ";
392
  if ((tios.c_cflag & CSIZE) == CS7)  os << " CS7   ";
393
  if ((tios.c_cflag & CSIZE) == CS8)  os << " CS8   ";
394
  if (tios.c_cflag & CSTOPB) os << " CSTOPB";
395
  if (tios.c_cflag & HUPCL)  os << " HUPCL ";
396
  if (tios.c_cflag & PARENB) os << " PARENB";
397
  if (tios.c_cflag & PARODD) os << " PARODD";
398
  speed_t speed = cfgetispeed(&tios);
399
  int baud = 0;
400 16 wfjm
  if (speed == B2400)    baud =    2400;
401
  if (speed == B4800)    baud =    4800;
402 10 wfjm
  if (speed == B9600)    baud =    9600;
403
  if (speed == B19200)   baud =   19200;
404
  if (speed == B38400)   baud =   38400;
405
  if (speed == B57600)   baud =   57600;
406
  if (speed == B115200)  baud =  115200;
407
  if (speed == B230400)  baud =  230400;
408
  if (speed == B460800)  baud =  460800;
409
  if (speed == B500000)  baud =  500000;
410
  if (speed == B921600)  baud =  921600;
411
  if (speed == B1000000) baud = 1000000;
412 16 wfjm
  if (speed == B1152000) baud = 1152000;
413
  if (speed == B1500000) baud = 1500000;
414 10 wfjm
  if (speed == B2000000) baud = 2000000;
415 16 wfjm
  if (speed == B2500000) baud = 2500000;
416 10 wfjm
  if (speed == B3000000) baud = 3000000;
417 16 wfjm
  if (speed == B3500000) baud = 3500000;
418
  if (speed == B4000000) baud = 4000000;
419 10 wfjm
  os << " speed: " << RosPrintf(baud, "d", 7);
420
  os << endl;
421
 
422
  os << bl << "  c_lflag : " << RosPrintf(tios.c_lflag,"x0",8);
423
  if (tios.c_lflag & ECHO)   os << " ECHO  ";
424
  if (tios.c_lflag & ECHOE)  os << " ECHOE ";
425
  if (tios.c_lflag & ECHOK)  os << " ECHOK ";
426
  if (tios.c_lflag & ECHONL) os << " ECHONL";
427
  if (tios.c_lflag & ICANON) os << " ICANON";
428
  if (tios.c_lflag & IEXTEN) os << " IEXTEN";
429
  if (tios.c_lflag & ISIG)   os << " ISIG  ";
430
  if (tios.c_lflag & NOFLSH) os << " NOFLSH";
431
  if (tios.c_lflag & TOSTOP) os << " TOSTOP";
432
  os << endl;
433
 
434
  os << bl << "  c_cc    : " << endl;
435
  os << bl << "    [VEOF]  : " << RosPrintf(tios.c_cc[VEOF],"o",3);
436
  os       << "    [VEOL]  : " << RosPrintf(tios.c_cc[VEOL],"o",3);
437
  os       << "    [VERASE]: " << RosPrintf(tios.c_cc[VERASE],"o",3);
438
  os       << "    [VINTR] : " << RosPrintf(tios.c_cc[VINTR],"o",3)  << endl;
439
  os << bl << "    [VKILL] : " << RosPrintf(tios.c_cc[VKILL],"o",3);
440
  os       << "    [VQUIT] : " << RosPrintf(tios.c_cc[VQUIT],"o",3);
441
  os       << "    [VSUSP] : " << RosPrintf(tios.c_cc[VSUSP],"o",3);
442
  os       << "    [VSTART]: " << RosPrintf(tios.c_cc[VSTART],"o",3) << endl;
443
  os << bl << "    [VSTOP] : " << RosPrintf(tios.c_cc[VSTOP],"o",3);
444
  os       << "    [VMIN]  : " << RosPrintf(tios.c_cc[VMIN],"o",3);
445
  os       << "    [VTIME] : " << RosPrintf(tios.c_cc[VTIME],"o",3)  << endl;
446
 
447
  return;
448
}
449
 
450 19 wfjm
} // end namespace Retro

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.