1 // Copyright (C) 2008-2011 David Sugar, Tycho Softworks.
2 //
3 // This file is part of GNU Bayonne.
4 //
5 // GNU Bayonne is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // GNU Bayonne is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with GNU Bayonne. If not, see <http://www.gnu.org/licenses/>.
17
18 #include <config.h>
19 #include <ucommon/ucommon.h>
20 #include <ucommon/export.h>
22
23 #if !defined(_MSWINDOWS_) && defined(HAVE_TERMIOS_H)
24 #include <termios.h>
25 #include <sys/select.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <errno.h>
29
30 #ifdef HAVE_SYS_IOCTL_H
31 #include <sys/ioctl.h>
32 #else
33 #include <ioctl.h>
34 #endif
35
37 using namespace UCOMMON_NAMESPACE;
38
39 class __LOCAL serial : public Serial
40 {
41 private:
42 fd_t fd;
43 struct termios original, current;
44
45 void restore(void);
46 bool set(const char *format);
47 void dtr(timeout_t timeout);
48 size_t get(void *data, size_t len);
49 size_t put(void *data, size_t len);
50 void bin(size_t size, timeout_t timeout);
51 void text(char nl1, char nl2);
52 void clear(void);
53 bool flush(timeout_t timeout);
54 bool wait(timeout_t timeout);
55 void sync(void);
56
57 operator bool();
58 bool operator!();
59
60 public:
61 serial(const char *name);
62 ~serial();
63 };
64
65 serial::serial(const char *name) : Serial()
66 {
67 fd = ::open(name, O_RDWR | O_NDELAY);
68 if(fd < 0) {
69 error = errno;
70 return;
71 }
72
73 long ioflags = fcntl(fd, F_GETFL);
74 tcgetattr(fd, ¤t);
75 tcgetattr(fd, &original);
76
77 current.c_oflag = current.c_lflag = 0;
78 current.c_cflag = CLOCAL | CREAD | HUPCL;
79 current.c_iflag = IGNBRK;
80
81 memset(¤t.c_cc, 0, sizeof(current.c_cc));
82 current.c_cc[VMIN] = 1;
83
84 cfsetispeed(¤t, cfgetispeed(&original));
85 cfsetospeed(¤t, cfgetospeed(&original));
86
87 current.c_cflag |= original.c_cflag & (CRTSCTS | CSIZE | PARENB | PARODD | CSTOPB);
88 current.c_iflag |= original.c_iflag & (IXON | IXANY | IXOFF);
89
90 tcsetattr(fd, TCSANOW, ¤t);
91 fcntl(fd, F_SETFL, ioflags & ~O_NDELAY);
92
93 #if defined(TIOCM_RTS) && defined(TIOCMODG)
94 int mcs = 0;
95 ioctl(fd, TIOCMODG, &mcs);
96 mcs |= TIOCM_RTS;
97 ioctl(fd, TIOCMODS, &mcs);
98 #endif
99 }
100
101 serial::~serial()
102 {
103 if(fd < 0)
104 return;
105
106 restore();
107 ::close(fd);
108 fd = -1;
109 }
110
111 serial::operator bool()
112 {
113 if(fd > -1)
114 return true;
115
116 return false;
117 }
118
119 bool serial::operator!()
120 {
121 if(fd < 0)
122 return true;
123
124 return false;
125 }
126
127 void serial::restore(void)
128 {
129 if(fd < 0)
130 return;
131
132 memcpy(¤t, &original, sizeof(current));
133 tcsetattr(fd, TCSANOW, ¤t);
134 }
135
136 bool serial::set(const char *format)
137 {
138 assert(format != NULL);
139
140 if(fd < 0)
141 return false;
142
143 unsigned long opt;
144 char buf[256];
145 String::set(buf, sizeof(buf), format);
146
147 char *cp = strtok(buf, ",");
148 while(cp) {
149 switch(*cp) {
150 case 'n':
151 case 'N':
152 current.c_cflag &= ~(PARENB | PARODD);
153 break;
154 case 'e':
155 case 'E':
156 current.c_cflag = (current.c_cflag & ~PARODD) | PARENB;
157 break;
158 case 'o':
159 case 'O':
160 current.c_cflag |= (PARODD | PARENB);
161 break;
162 case 's':
163 case 'S':
164 current.c_cflag &= ~CRTSCTS;
165 current.c_cflag |= (IXON | IXANY | IXOFF);
166 break;
167 case 'h':
168 case 'H':
169 current.c_cflag |= CRTSCTS;
170 current.c_cflag &= ~(IXON | IXANY | IXOFF);
171 break;
172 case 'b':
173 case 'B':
174 current.c_cflag |= CRTSCTS;
175 current.c_cflag |= (IXON | IXANY | IXOFF);
176 break;
177 case '0':
178 case '1':
179 case '2':
180 case '3':
181 case '4':
182 case '5':
183 case '6':
184 case '7':
185 case '8':
186 case '9':
187 opt = atol(cp);
188 switch(opt) {
189 case 1:
190 current.c_cflag &= ~CSTOPB;
191 break;
192 case 2:
193 current.c_cflag |= CSTOPB;
194 break;
195 case 5:
196 current.c_cflag = (current.c_cflag & ~CSIZE) | CS5;
197 break;
198 case 6:
199 current.c_cflag = (current.c_cflag & ~CSIZE) | CS6;
200 break;
201 case 7:
202 current.c_cflag = (current.c_cflag & ~CSIZE) | CS7;
203 break;
204 case 8:
205 current.c_cflag = (current.c_cflag & ~CSIZE) | CS8;
206 break;
207 #ifdef B921600
208 case 921600l:
209 cfsetispeed(¤t, B921600);
210 cfsetospeed(¤t, B921600);
211 break;
212 #endif
213 #ifdef B576000
214 case 576000l:
215 cfsetispeed(¤t, B576000);
216 cfsetospeed(¤t, B576000);
217 break;
218 #endif
219 #ifdef B500000
220 case 500000l:
221 cfsetispeed(¤t, B500000);
222 cfsetospeed(¤t, B500000);
223 break;
224 #endif
225 #ifdef B460800
226 case 460800l:
227 cfsetispeed(¤t, B460800);
228 cfsetospeed(¤t, B460800);
229 break;
230 #endif
231 #ifdef B230400
232 case 230400l:
233 cfsetispeed(¤t, B230400);
234 cfsetospeed(¤t, B230400);
235 break;
236 #endif
237 #ifdef B115200
238 case 115200l:
239 cfsetispeed(¤t, B115200);
240 cfsetospeed(¤t, B115200);
241 break;
242 #endif
243 #ifdef B57600
244 case 57600l:
245 cfsetispeed(¤t, B57600);
246 cfsetospeed(¤t, B57600);
247 break;
248 #endif
249 #ifdef B38400
250 case 38400l:
251 cfsetispeed(¤t, B38400);
252 cfsetospeed(¤t, B38400);
253 break;
254 #endif
255 case 19200l:
256 cfsetispeed(¤t, B19200);
257 cfsetospeed(¤t, B19200);
258 break;
259 case 9600l:
260 cfsetispeed(¤t, B9600);
261 cfsetospeed(¤t, B9600);
262 break;
263 case 4800l:
264 cfsetispeed(¤t, B4800);
265 cfsetospeed(¤t, B4800);
266 break;
267 case 2400l:
268 cfsetispeed(¤t, B2400);
269 cfsetospeed(¤t, B2400);
270 break;
271 case 1200l:
272 cfsetispeed(¤t, B1200);
273 cfsetospeed(¤t, B1200);
274 break;
275 case 600l:
276 cfsetispeed(¤t, B600);
277 cfsetospeed(¤t, B600);
278 break;
279 case 300l:
280 cfsetispeed(¤t, B300);
281 cfsetospeed(¤t, B300);
282 break;
283 case 110l:
284 cfsetispeed(¤t, B110);
285 cfsetospeed(¤t, B110);
286 break;
287 #ifdef B200
288 case 200l:
289 cfsetispeed(¤t, B200);
290 cfsetospeed(¤t, B200);
291 break;
292 #endif
293 #ifdef B150
294 case 150l:
295 cfsetispeed(¤t, B150);
296 cfsetospeed(¤t, B150);
297 break;
298 #endif
299 #ifdef B134
300 case 134l:
301 cfsetispeed(¤t, B134);
302 cfsetospeed(¤t, B134);
303 break;
304 #endif
305 #ifdef B0
306 case 0:
307 cfsetispeed(¤t, B0);
308 cfsetospeed(¤t, B0);
309 break;
310 #endif
311 default:
312 error = EINVAL;
313 return false;
314 }
315 break;
316 default:
317 error = EINVAL;
318 return false;
319 }
320 cp = strtok(NULL, ",");
321 }
322 tcsetattr(fd, TCSANOW, ¤t);
323 return true;
324 }
325
326 void serial::dtr(timeout_t timeout)
327 {
328 if(fd < 0)
329 return;
330
331 struct termios tty, old;
332 tcgetattr(fd, &tty);
333 tcgetattr(fd, &old);
334 cfsetospeed(&tty, B0);
335 cfsetispeed(&tty, B0);
336 tcsetattr(fd, TCSANOW, &tty);
337
338 if(timeout) {
339 Thread::sleep(timeout);
340 tcsetattr(fd, TCSANOW, &old);
341 }
342 }
343
344 size_t serial::get(void *data, size_t len)
345 {
346 if(fd < 0)
347 return 0;
348
349 ssize_t rts = ::read(fd, data, len);
350 if(rts < 0) {
351 error = errno;
352 return 0;
353 }
354 return rts;
355 }
356
357 size_t serial::put(void *data, size_t len)
358 {
359 if(fd < 0)
360 return 0;
361
362 ssize_t rts = ::write(fd, data, len);
363 if(rts < 0) {
364 error = errno;
365 return 0;
366 }
367 return rts;
368 }
369
370 void serial::bin(size_t size, timeout_t timeout)
371 {
372 if(fd < 0)
373 return;
374
375 #ifdef _PC_MAX_INPUT
376 int max = fpathconf(fd, _PC_MAX_INPUT);
377 #else
378 int max = MAX_INPUT;
379 #endif
380 if(size > (size_t)max)
381 size = max;
382
383 current.c_cc[VEOL] = current.c_cc[VEOL2] = 0;
384 current.c_cc[VMIN] = (unsigned char)size;
385 current.c_cc[VTIME] = (unsigned char)( (timeout + 99l) / 100l);
386 current.c_lflag &= ~ICANON;
387 tcsetattr(fd, TCSANOW, ¤t);
388 }
389
390 void serial::text(char nl1, char nl2)
391 {
392 current.c_cc[VMIN] = current.c_cc[VTIME] = 0;
393 current.c_cc[VEOL] = nl1;
394 current.c_cc[VEOL2] = nl2;
395 current.c_lflag |= ICANON;
396 tcsetattr(fd, TCSANOW, ¤t);
397 }
398
399 void serial::clear(void)
400 {
401 tcflush(fd, TCIFLUSH);
402 }
403
404 void serial::sync(void)
405 {
406 tcdrain(fd);
407 }
408
409 bool serial::wait(timeout_t timeout)
410 {
411 struct timeval tv;
412 fd_set grp;
413 struct timeval *tvp = &tv;
414 int status;
415
416 if(fd < 0)
417 return false;
418
419 if(timeout == Timer::inf)
420 tvp = NULL;
421 else {
422 tv.tv_usec = (timeout % 1000) * 1000;
423 tv.tv_sec = timeout / 1000;
424 }
425
426 FD_ZERO(&grp);
427 FD_SET(fd, &grp);
428 status = select(fd + 1, &grp, NULL, NULL, tvp);
429 if(status < 0)
430 error = errno;
431
432 if(status < 1)
433 return false;
434
435 if(FD_ISSET(fd, &grp))
436 return true;
437
438 return false;
439 }
440
441 bool serial::flush(timeout_t timeout)
442 {
443 struct timeval tv;
444 fd_set grp;
445 struct timeval *tvp = &tv;
446 int status;
447 bool rtn = false;
448
449 if(fd < 0)
450 return false;
451
452 if(timeout == Timer::inf)
453 tvp = NULL;
454 else {
455 tv.tv_usec = (timeout % 1000) * 1000;
456 tv.tv_sec = timeout / 1000;
457 }
458
459 FD_ZERO(&grp);
460 FD_SET(fd, &grp);
461 status = select(fd + 1, NULL, &grp, NULL, tvp);
462 if(status < 0) {
463 error = errno;
464 return false;
465 }
466
467 if(status > 0 && FD_ISSET(fd, &grp))
468 rtn = true;
469
470 if(rtn)
471 tcflush(fd, TCOFLUSH);
472
473 return rtn;
474 }
475
476 Serial *Serial::create(const char *name)
477 {
478 assert(name != NULL);
479
480 char buf[64];
481 fsys::fileinfo_t ino;
482
483 if(*name == '/') {
484 String::set(buf, sizeof(buf), name);
485 goto check;
486 }
487
488 if(eq(name, "tty", 3))
489 snprintf(buf, sizeof(buf), "/dev/%s", name);
490 else
491 snprintf(buf, sizeof(buf), "/dev/tty%s", name);
492
493 check:
494 char *cp = strchr(buf, ':');
495 if(cp)
496 *cp = 0;
497 fsys::fileinfo(buf, &ino);
498 if(!fsys::ischar(&ino))
499 return NULL;
500 serial_t dev = new serial(buf);
501 name = strchr(name, ':');
502 if(dev && name)
503 dev->set(++name);
504 return dev;
505 }
506
507 stringpager *Serial::list(void)
508 {
509 stringpager *list = new stringpager;
510 char filename[64];
511 fsys_t dir;
512
513 fsys::open(dir, "/dev", fsys::ACCESS_DIRECTORY);
514 while(is(dir) && fsys::read(dir, filename, sizeof(filename)) > 0) {
515 if(eq(filename, "tty", 3))
516 list->add(filename + 3);
517 }
518 fsys::close(dir);
519 }
520
521 #endif
522
523
#define BAYONNE_NAMESPACE
GNU Bayonne library namespace.