]>
Commit | Line | Data |
---|---|---|
0a6aabe7 JH |
1 | #include <stdio.h> |
2 | #include <string.h> | |
3 | #include <unistd.h> | |
4 | #include <fcntl.h> | |
5 | #include <sys/ioctl.h> | |
6 | #include <errno.h> | |
7 | #include <paths.h> | |
8 | #include <sysexits.h> | |
9 | #include <termios.h> | |
10 | #include <sys/param.h> | |
11 | #include <sys/select.h> | |
12 | #include <sys/time.h> | |
13 | #include <time.h> | |
14 | ||
18284ae7 WW |
15 | #include "serial/impl/unix.h" |
16 | ||
0a6aabe7 JH |
17 | #ifndef TIOCINQ |
18 | #ifdef FIONREAD | |
19 | #define TIOCINQ FIONREAD | |
20 | #else | |
21 | #define TIOCINQ 0x541B | |
22 | #endif | |
23 | #endif | |
24 | ||
48a30ec4 WW |
25 | class UnhandledException : public std::exception { |
26 | const char * e_what; | |
27 | public: | |
28 | UnhandledException(const char * e_what) {this->e_what = e_what;} | |
29 | ||
30 | virtual const char* what() const throw() { | |
31 | std::stringstream ss; | |
32 | ss << "Unhandled Exception: " << this->e_what; | |
33 | return ss.str().c_str(); | |
34 | } | |
35 | }; | |
36 | ||
37 | typedef UnhandledException e; | |
38 | ||
0a6aabe7 JH |
39 | using ::serial::Serial; |
40 | using std::string; | |
18284ae7 | 41 | |
0a6aabe7 | 42 | Serial::SerialImpl::SerialImpl (const string &port, int baudrate, |
65fc8fb2 WW |
43 | long timeout, bytesize_t bytesize, |
44 | parity_t parity, stopbits_t stopbits, | |
45 | flowcontrol_t flowcontrol) | |
46 | : fd_(-1), isOpen_(false), interCharTimeout_(-1), port_(port), | |
47 | baudrate_(baudrate), timeout_(timeout), bytesize_(bytesize), | |
48 | parity_(parity), stopbits_(stopbits), flowcontrol_(flowcontrol) | |
18284ae7 | 49 | { |
65fc8fb2 | 50 | if (port_.empty() == false) this->open(); |
18284ae7 WW |
51 | } |
52 | ||
0a6aabe7 JH |
53 | Serial::SerialImpl::~SerialImpl () { |
54 | this->close(); | |
18284ae7 WW |
55 | } |
56 | ||
57 | void | |
0a6aabe7 | 58 | Serial::SerialImpl::open () { |
48a30ec4 WW |
59 | if (port_.empty() == true) throw e("error"); |
60 | if (isOpen_ == true) throw e("error"); | |
61 | ||
65fc8fb2 | 62 | fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); |
48a30ec4 | 63 | |
65fc8fb2 | 64 | if (fd_ == -1) throw "Error"; |
48a30ec4 | 65 | |
65fc8fb2 WW |
66 | reconfigurePort(); |
67 | isOpen_ = true; | |
18284ae7 WW |
68 | } |
69 | ||
70 | void | |
0a6aabe7 | 71 | Serial::SerialImpl::reconfigurePort () { |
48a30ec4 | 72 | if (fd_ == -1) throw e("Error"); // Can only operate on a valid file descriptor |
65fc8fb2 WW |
73 | |
74 | struct termios options; // The current options for the file descriptor | |
75 | struct termios originalTTYAttrs; // The orignal file descriptor options | |
76 | ||
0a6aabe7 JH |
77 | uint8_t vmin = 0, vtime = 0; // timeout is done via select |
78 | if (interCharTimeout_ == -1) { | |
65fc8fb2 WW |
79 | vmin = 1; |
80 | vtime = int(interCharTimeout_ * 10); | |
81 | } | |
82 | ||
48a30ec4 | 83 | if (tcgetattr(fd_, &originalTTYAttrs) == -1) throw e("Error"); |
65fc8fb2 WW |
84 | |
85 | options = originalTTYAttrs; | |
86 | ||
0a6aabe7 JH |
87 | // set up raw mode / no echo / binary |
88 | options.c_cflag |= (CLOCAL|CREAD); | |
89 | options.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL| | |
90 | ISIG|IEXTEN); //|ECHOPRT | |
91 | ||
92 | options.c_oflag &= ~(OPOST); | |
93 | options.c_iflag &= ~(INLCR|IGNCR|ICRNL|IGNBRK); | |
94 | #ifdef IUCLC | |
95 | options.c_iflag &= ~IUCLC; | |
96 | #endif | |
97 | #ifdef PARMRK | |
65fc8fb2 | 98 | options.c_iflag &= ~PARMRK; |
0a6aabe7 JH |
99 | #endif |
100 | ||
101 | // setup baud rate | |
65fc8fb2 WW |
102 | // TODO(ash_git): validate baud rate |
103 | cfsetspeed(&options, baudrate_); | |
0a6aabe7 JH |
104 | |
105 | // setup char len | |
106 | options.c_cflag &= ~CSIZE; | |
107 | if (bytesize_ == EIGHTBITS) | |
108 | options.c_cflag |= CS8; | |
109 | else if (bytesize_ == SEVENBITS) | |
110 | options.c_cflag |= CS7; | |
111 | else if (bytesize_ == SIXBITS) | |
112 | options.c_cflag |= CS6; | |
113 | else if (bytesize_ == FIVEBITS) | |
114 | options.c_cflag |= CS5; | |
115 | else | |
48a30ec4 | 116 | throw e("ValueError(Invalid char len: %%r)"); |
0a6aabe7 JH |
117 | // setup stopbits |
118 | if (stopbits_ == STOPBITS_ONE) | |
119 | options.c_cflag &= ~(CSTOPB); | |
120 | else if (stopbits_ == STOPBITS_ONE_POINT_FIVE) | |
121 | options.c_cflag |= (CSTOPB); // XXX same as TWO.. there is no POSIX support for 1.5 | |
122 | else if (stopbits_ == STOPBITS_TWO) | |
123 | options.c_cflag |= (CSTOPB); | |
65fc8fb2 | 124 | else |
48a30ec4 | 125 | throw e("ValueError(Invalid stop bit specification:)"); |
0a6aabe7 JH |
126 | // setup parity |
127 | options.c_iflag &= ~(INPCK|ISTRIP); | |
128 | if (parity_ == PARITY_NONE) { | |
129 | options.c_cflag &= ~(PARENB|PARODD); | |
130 | } | |
65fc8fb2 | 131 | else if (parity_ == PARITY_EVEN) { |
0a6aabe7 JH |
132 | options.c_cflag &= ~(PARODD); |
133 | options.c_cflag |= (PARENB); | |
134 | } | |
65fc8fb2 | 135 | else if (parity_ == PARITY_ODD) { |
0a6aabe7 JH |
136 | options.c_cflag |= (PARENB|PARODD); |
137 | } | |
65fc8fb2 | 138 | else { |
48a30ec4 | 139 | throw e("ValueError(Invalid parity:"); |
65fc8fb2 | 140 | } |
0a6aabe7 JH |
141 | // setup flow control |
142 | // xonxoff | |
143 | #ifdef IXANY | |
144 | if (xonxoff_) | |
145 | options.c_iflag |= (IXON|IXOFF); //|IXANY) | |
146 | else | |
147 | options.c_iflag &= ~(IXON|IXOFF|IXANY); | |
148 | #else | |
149 | if (xonxoff_) | |
150 | options.c_iflag |= (IXON|IXOFF); | |
151 | else | |
152 | options.c_iflag &= ~(IXON|IXOFF); | |
153 | #endif | |
154 | // rtscts | |
155 | #ifdef CRTSCTS | |
156 | if (rtscts_) | |
157 | options.c_cflag |= (CRTSCTS); | |
158 | else | |
159 | options.c_cflag &= ~(CRTSCTS); | |
160 | #elif defined CNEW_RTSCTS | |
161 | if (rtscts_) | |
65fc8fb2 | 162 | options.c_cflag |= (CNEW_RTSCTS); |
0a6aabe7 JH |
163 | else |
164 | options.c_cflag &= ~(CNEW_RTSCTS); | |
165 | #else | |
166 | #error "OS Support seems wrong." | |
167 | #endif | |
168 | ||
169 | // buffer | |
170 | // vmin "minimal number of characters to be read. = for non blocking" | |
171 | options.c_cc[VMIN] = vmin; | |
172 | // vtime | |
173 | options.c_cc[VTIME] = vtime; | |
174 | ||
175 | // activate settings | |
65fc8fb2 | 176 | ::tcsetattr(fd_, TCSANOW, &options); |
18284ae7 WW |
177 | } |
178 | ||
0a6aabe7 JH |
179 | void |
180 | Serial::SerialImpl::close () { | |
65fc8fb2 WW |
181 | if (isOpen_ == true) { |
182 | if (fd_ != -1) { | |
183 | ::close(fd_); | |
184 | fd_ = -1; | |
185 | } | |
186 | isOpen_ = false; | |
187 | } | |
18284ae7 WW |
188 | } |
189 | ||
0a6aabe7 JH |
190 | bool |
191 | Serial::SerialImpl::isOpen () { | |
192 | return isOpen_; | |
18284ae7 WW |
193 | } |
194 | ||
195 | size_t | |
0a6aabe7 | 196 | Serial::SerialImpl::available () { |
65fc8fb2 WW |
197 | if (!isOpen_) { |
198 | return 0; | |
199 | } | |
200 | int count = 0; | |
201 | int result = ioctl(fd_, TIOCINQ, &count); | |
202 | if (result == 0) { | |
203 | return count; | |
204 | } else { | |
48a30ec4 | 205 | throw e("Error"); |
65fc8fb2 | 206 | } |
18284ae7 WW |
207 | } |
208 | ||
0a6aabe7 JH |
209 | string |
210 | Serial::SerialImpl::read (size_t size) { | |
48a30ec4 | 211 | if (!isOpen_) throw e("PortNotOpenError()"); |
65fc8fb2 WW |
212 | string message = ""; |
213 | char buf[1024]; | |
214 | fd_set readfds; | |
0a6aabe7 | 215 | while (message.length() < size) { |
65fc8fb2 WW |
216 | FD_ZERO(&readfds); |
217 | FD_SET(fd_, &readfds); | |
218 | struct timeval timeout; | |
219 | timeout.tv_sec = timeout_ / 1000; | |
220 | timeout.tv_usec = timeout_ % 1000; | |
221 | int r = select(1, &readfds, NULL, NULL, &timeout); | |
222 | ||
223 | if (r == -1 && errno == EINTR) | |
224 | continue; | |
225 | ||
226 | if (r == -1) { | |
0a6aabe7 JH |
227 | perror("select()"); |
228 | exit(EXIT_FAILURE); | |
229 | } | |
230 | ||
231 | if (FD_ISSET(fd_, &readfds)) { | |
65fc8fb2 WW |
232 | memset(buf, 0, 1024); |
233 | size_t bytes_read = ::read(fd_, buf, 1024); | |
234 | // read should always return some data as select reported it was | |
235 | // ready to read when we get to this point. | |
236 | if (bytes_read < 1) { | |
237 | // Disconnected devices, at least on Linux, show the | |
238 | // behavior that they are always ready to read immediately | |
239 | // but reading returns nothing. | |
48a30ec4 | 240 | throw e("SerialException('device reports readiness to read but returned no data (device disconnected?)')"); |
65fc8fb2 WW |
241 | } |
242 | message.append(buf, bytes_read); | |
243 | } | |
244 | else { | |
245 | break; // Timeout | |
246 | } | |
247 | } | |
0a6aabe7 | 248 | return message; |
18284ae7 WW |
249 | } |
250 | ||
251 | size_t | |
0a6aabe7 | 252 | Serial::SerialImpl::write (const string &data) { |
48a30ec4 | 253 | if (isOpen_ == false) throw e("portNotOpenError"); |
65fc8fb2 | 254 | |
f14ac390 | 255 | size_t t = data.length(); |
65fc8fb2 WW |
256 | size_t n = ::write(fd_, data.c_str(), data.length()); |
257 | if (n == -1) { | |
48a30ec4 | 258 | throw e("Write error"); |
65fc8fb2 | 259 | } |
f14ac390 | 260 | return n; |
18284ae7 WW |
261 | } |
262 | ||
263 | void | |
0a6aabe7 JH |
264 | Serial::SerialImpl::setPort (const string &port) { |
265 | port_ = port; | |
18284ae7 WW |
266 | } |
267 | ||
0a6aabe7 JH |
268 | string |
269 | Serial::SerialImpl::getPort () const { | |
270 | return port_; | |
18284ae7 WW |
271 | } |
272 | ||
273 | void | |
0a6aabe7 | 274 | Serial::SerialImpl::setTimeout (long timeout) { |
f14ac390 | 275 | timeout_ = timeout; |
18284ae7 WW |
276 | } |
277 | ||
278 | long | |
0a6aabe7 | 279 | Serial::SerialImpl::getTimeout () const { |
f14ac390 | 280 | return timeout_; |
18284ae7 WW |
281 | } |
282 | ||
283 | void | |
0a6aabe7 | 284 | Serial::SerialImpl::setBaudrate (int baudrate) { |
65fc8fb2 WW |
285 | baudrate_ = baudrate; |
286 | reconfigurePort(); | |
18284ae7 WW |
287 | } |
288 | ||
289 | int | |
0a6aabe7 JH |
290 | Serial::SerialImpl::getBaudrate () const { |
291 | return baudrate_; | |
18284ae7 WW |
292 | } |
293 | ||
294 | void | |
0a6aabe7 | 295 | Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize) { |
65fc8fb2 | 296 | bytesize_ = bytesize; |
18284ae7 WW |
297 | } |
298 | ||
0a6aabe7 JH |
299 | serial::bytesize_t |
300 | Serial::SerialImpl::getBytesize () const { | |
f14ac390 | 301 | return bytesize_; |
18284ae7 WW |
302 | } |
303 | ||
304 | void | |
0a6aabe7 | 305 | Serial::SerialImpl::setParity (serial::parity_t parity) { |
f14ac390 | 306 | parity_ = parity; |
18284ae7 WW |
307 | } |
308 | ||
0a6aabe7 JH |
309 | serial::parity_t |
310 | Serial::SerialImpl::getParity () const { | |
f14ac390 | 311 | return parity_; |
18284ae7 WW |
312 | } |
313 | ||
314 | void | |
0a6aabe7 | 315 | Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits) { |
f14ac390 | 316 | stopbits_ = stopbits; |
18284ae7 WW |
317 | } |
318 | ||
0a6aabe7 JH |
319 | serial::stopbits_t |
320 | Serial::SerialImpl::getStopbits () const { | |
f14ac390 | 321 | return stopbits_; |
18284ae7 WW |
322 | } |
323 | ||
324 | void | |
0a6aabe7 | 325 | Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol) { |
f14ac390 | 326 | flowcontrol_ = flowcontrol; |
18284ae7 WW |
327 | } |
328 | ||
0a6aabe7 JH |
329 | serial::flowcontrol_t |
330 | Serial::SerialImpl::getFlowcontrol () const { | |
f14ac390 | 331 | return flowcontrol_; |
18284ae7 WW |
332 | } |
333 | ||
334 | ||
335 | ||
336 | ||
337 | ||
338 |