BOROSOFTWAREAll work →
scantool/diag_tty_unix.c 1361 lines
1/*
2 * diag_tty_unix.c
3 *
4 * This file is part of freediag - Vehicle Diagnostic Utility
5 *
6 * Copyright (C) 2001-2004 Richard Almeida & others
7 * Copyright (C) 2004 Steve Baker <[email protected]>
8 * Copyright (C) 2004 Steve Meisner <[email protected]>
9 * Copyright (C) 2004 Vasco Nevoa <[email protected]>
10 * Copyright (C) 2011-2016 fenugrec <[email protected]>
11 * Copyright (C) 2015 Tomasz Kaźmierczak <[email protected]>
12 *
13 * This program is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 */
27
28#include <assert.h>
29#include <sys/types.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <dirent.h>
33#include <unistd.h>
34
35#include <stdbool.h>
36#include <stdint.h>
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40#include <signal.h>
41
42#include "diag.h"
43#include "diag_l0.h"
44#include "diag_os.h"
45#include "diag_err.h"
46#include "diag_tty.h"
47#include "diag_tty_unix.h"
48
49#if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO)
50#define PT_REPEAT 1000 //after the nominal timeout period the timer will expire every PT_REPEAT us.
51static void diag_tty_rw_timeout_handler(UNUSED(int sig), siginfo_t *si, UNUSED(void *uc)) {
52 assert(si->si_value.sival_ptr != NULL);
53 ((struct unix_tty_int *)(si->si_value.sival_ptr))->pt_expired = 1;
54 return;
55}
56#endif
57
58ttyp *diag_tty_open(const char *portname) {
59 int rv;
60 struct unix_tty_int *uti;
61#if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO)
62 struct sigevent to_sigev;
63 struct sigaction sa;
64 clockid_t timeout_clkid;
65#endif
66
67 assert(portname);
68
69 rv = diag_calloc(&uti,1);
70 if (rv != 0) {
71 return diag_pseterr(rv);
72 }
73
74#if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO)
75 //set-up the r/w timeouts clock - here we just create it; it will be armed when needed
76 #ifdef _POSIX_MONOTONIC_CLOCK
77 timeout_clkid = CLOCK_MONOTONIC;
78 #else
79 timeout_clkid = CLOCK_REALTIME;
80 #endif // _POSIX_MONOTONIC_CLOCK
81 sa.sa_flags = SA_SIGINFO;
82 sa.sa_sigaction = diag_tty_rw_timeout_handler;
83 sigemptyset(&sa.sa_mask);
84 if (sigaction(SIGUSR1, &sa, NULL) != 0) {
85 fprintf(stderr, FLFMT "Could not set-up action for timeout timer... report this\n", FL);
86 free(uti);
87 return diag_pseterr(DIAG_ERR_GENERAL);
88 }
89
90 to_sigev.sigev_notify = SIGEV_SIGNAL;
91 to_sigev.sigev_signo = SIGUSR1;
92 to_sigev.sigev_value.sival_ptr = uti;
93 if (timer_create(timeout_clkid, &to_sigev, &uti->timerid) != 0) {
94 fprintf(stderr, FLFMT "Could not create timeout timer... report this\n", FL);
95 free(uti);
96 return diag_pseterr(DIAG_ERR_GENERAL);
97 }
98#endif
99
100 uti->fd = DL0D_INVALIDHANDLE;
101
102 size_t n = strlen(portname) + 1;
103
104 if ((rv=diag_malloc(&uti->name, n))) {
105 free(uti);
106 return diag_pseterr(rv);
107 }
108 strncpy(uti->name, portname, n);
109
110 //past this point, we can call diag_tty_close(uti) to abort in case of errors
111
112 errno = 0;
113
114#if defined(O_NONBLOCK) && (SEL_TTYOPEN==S_ALT1 || SEL_TTYOPEN==S_AUTO)
115 /*
116 * For POSIX behavior: Open serial device non-blocking to avoid
117 * modem control issues, then set to blocking.
118 */
119 {
120 int fl;
121 uti->fd = open(uti->name, O_RDWR | O_NONBLOCK);
122
123 if (uti->fd > 0) {
124 errno = 0;
125 if ((fl = fcntl(uti->fd, F_GETFL, 0)) < 0) {
126 fprintf(stderr,
127 FLFMT "Can't get flags with fcntl on fd %d: %s.\n",
128 FL, uti->fd, strerror(errno));
129 diag_tty_close(uti);
130 return diag_pseterr(DIAG_ERR_GENERAL);
131 }
132 fl &= ~O_NONBLOCK;
133 errno = 0;
134 if (fcntl(uti->fd, F_SETFL, fl) < 0) {
135 fprintf(stderr,
136 FLFMT "Can't set flags with fcntl on fd %d: %s.\n",
137 FL, uti->fd, strerror(errno));
138 diag_tty_close(uti);
139 return diag_pseterr(DIAG_ERR_GENERAL);
140 }
141 }
142 }
143#else
144 #ifndef O_NONBLOCK
145 #warning No O_NONBLOCK on your system ?! Please report this
146 #endif
147 uti->fd = open(uti->name, O_RDWR);
148
149#endif // O_NONBLOCK
150
151 if (uti->fd >= 0) {
152 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V,
153 FLFMT "Device %s opened, fd %d\n",
154 FL, uti->name, uti->fd);
155 } else {
156 fprintf(stderr,
157 FLFMT "Could not open \"%s\" : %s. "
158 "Make sure the device specified corresponds to the "
159 "serial device your interface is connected to.\n",
160 FL, uti->name, strerror(errno));
161 diag_tty_close(uti);
162 return diag_pseterr(DIAG_ERR_GENERAL);
163 }
164
165 /*
166 * Save original settings so can reset
167 * device on close - we also set "current" settings to
168 * those we just read aswell
169 */
170
171#if defined(__linux__)
172 if (ioctl(uti->fd, TIOCGSERIAL, &uti->ss_orig) < 0) {
173 fprintf(stderr,
174 FLFMT "open: TIOCGSERIAL failed: %s\n", FL, strerror(errno));
175 uti->tioc_works = 0;
176 } else {
177 uti->ss_cur = uti->ss_orig;
178 uti->tioc_works = 1;
179 }
180#endif
181
182 if (ioctl(uti->fd, TIOCMGET, &uti->modemflags) < 0) {
183 fprintf(stderr,
184 FLFMT "open: TIOCMGET failed: %s\n", FL, strerror(errno));
185 diag_tty_close(uti);
186 return diag_pseterr(DIAG_ERR_GENERAL);
187 }
188
189#ifdef USE_TERMIOS2
190 rv = ioctl(uti->fd, TCGETS, &uti->st_orig);
191#else
192 rv = tcgetattr(uti->fd, &uti->st_orig);
193#endif
194 if (rv != 0) {
195 fprintf(stderr, FLFMT "open: could not get orig settings: %s\n",
196 FL, strerror(errno));
197 diag_tty_close(uti);
198 return diag_pseterr(DIAG_ERR_GENERAL);
199 }
200
201 //and set common flags
202 uti->st_cur = uti->st_orig;
203
204 /* "stty raw"-like iflag settings: */
205 /* Clear a bunch of un-needed flags */
206 uti->st_cur.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK
207 | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF
208 | IXANY | IMAXBEL);
209#ifdef __linux__
210 uti->st_cur.c_iflag &= ~(IUCLC); /* non-posix; disable ucase/lcase conversion */
211#endif
212
213 uti->st_cur.c_oflag &= ~(OPOST); //disable impl-defined output processing
214
215 /* Disable canonical input and keyboard signals.
216 +* There is no need to also clear the many ECHOXX flags, both because
217 +* many systems have non-POSIX flags and also because the ECHO
218 +* flags don't don't matter when ICANON is clear.
219 */
220 /* CJH: However, taking 'man termios' at its word, the ECHO flag is
221 * not* affected by ICANON, and it seems we do need to clear it */
222 uti->st_cur.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN);
223
224 uti->st_cur.c_cflag &= ~(CRTSCTS); //non-posix; disables hardware flow ctl
225 uti->st_cur.c_cflag |= (CLOCAL | CREAD); //ignore modem control lines; enable read
226
227 uti->st_cur.c_cc[VMIN] = 1; //Minimum # of bytes before read() returns (default: 0!!!)
228
229 //and update termios with new flags.
230#ifdef USE_TERMIOS2
231 rv = ioctl(uti->fd, TCSETS, &uti->st_cur);
232 rv |= ioctl(uti->fd, TCGETS2, &uti->st2_cur);
233#else
234 rv=tcsetattr(uti->fd, TCSAFLUSH, &uti->st_cur);
235#endif
236 if (rv != 0) {
237 fprintf(stderr, FLFMT "open: can't set input flags: %s.\n",
238 FL, strerror(errno));
239 diag_tty_close(uti);
240 return diag_pseterr(DIAG_ERR_GENERAL);
241 }
242
243 //arbitrarily set the single byte write timeout to 1ms
244 uti->byte_write_timeout_us = 1000ul;
245
246 return uti;
247}
248
249/* Close up the TTY and restore. */
250void diag_tty_close(ttyp *tty_int) {
251 struct unix_tty_int *uti = tty_int;
252
253 if (!uti) {
254 return;
255 }
256 if (uti->name) {
257 free(uti->name);
258 }
259#if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO)
260 timer_delete(uti->timerid);
261#endif
262 if (uti->fd != DL0D_INVALIDHANDLE) {
263#if defined(__linux__)
264 if (uti->tioc_works) {
265 (void)ioctl(uti->fd, TIOCSSERIAL, &uti->ss_orig);
266 }
267#endif
268#ifdef USE_TERMIOS2
269 (void)ioctl(uti->fd, TCSETS2, &uti->st2_orig);
270#else
271 (void)tcsetattr(uti->fd, TCSADRAIN, &uti->st_orig);
272#endif
273 (void)ioctl(uti->fd, TIOCMSET, &uti->modemflags);
274 (void)close(uti->fd);
275 }
276
277 free(uti);
278
279 return;
280}
281
282
283/* Baud rate hell. Status in 2015 seems to be :
284 - termios2 struct + BOTHER flag + TCSETS2 ioctl; questionable availability
285 - ASYNC_SPD_CUST + B38400 + custom divisor trick is "deprecated",
286 and needs the linux TIOCSSERIAL ioctl (far from ubiquitous)
287 - take a chance with cfsetispeed etc. with an integer argument, if
288 B9600 == 9600 (non standard, shot in the dark except maybe on BSD??)
289 - use nearest standard speed + cfsetispeed
290 - OSX >10.4 : (unconfirmed, TODO) : IOSSIOSPEED ioctl ?
291 - BSD ? (unconfirmed, TODO) : IOSSIOSPEED ioctl ?
292 */
293/** internal use : _tty_setspeed, used inside diag_tty_setup.
294 *
295 * @return actual new speed, or 0 if failed.
296 * updates ->tty_int struct;
297 * should probably called last (i.e. after setting other flags)
298 */
299static int _tty_setspeed(ttyp *tty_int, unsigned int spd) {
300 struct unix_tty_int *uti = tty_int;
301 unsigned int spd_real; //validate baud rate precision
302 struct termios st_new;
303 int spd_done=0; //flag success
304 int rv = 1;
305 int fd;
306
307 fd = uti->fd;
308
309 const unsigned int std_table[] = {
310 0, 50, 75, 110,
311 134, 150, 200, 300,
312 600, 1200, 1800, 2400,
313 4800, 9600, 19200, 38400,
314 #ifdef B57600
315 57600,
316 #endif
317 #ifdef B115200
318 115200
319 #endif
320 };
321 //std_names must match speeds in std_table !
322 const speed_t std_names[] = {
323 B0, B50, B75, B110,
324 B134, B150, B200, B300,
325 B600, B1200, B1800, B2400,
326 B4800, B9600, B19200, B38400,
327 #ifdef B57600
328 B57600,
329 #endif
330 #ifdef B115200
331 B115200
332 #endif
333 };
334
335 st_new = uti->st_cur;
336
337#if defined(__linux__) && defined(USE_TERMIOS2)
338 /* Try setting BOTHER flag in .c_cflag, and literal speed in .c_ispeed */
339 while (!spd_done) {
340 struct termios2 st2;
341 st2 = uti->st2_cur;
342 st2.c_cflag &= ~CBAUD;
343 st2.c_cflag |= BOTHER;
344 st2.c_ispeed = spd;
345 st2.c_ospeed = spd;
346 if ((rv = ioctl(fd, TCSETS2, &st2)) != 0) {
347 break;
348 }
349 //re-read to get actual speed
350 if ((rv = ioctl(fd, TCGETS2, &uti->st2_cur)) != 0) {
351 break;
352 }
353 spd_real = uti->st2_cur.c_ospeed;
354 spd_done = 1;
355 //Setting other flags without termios2 would "erase" speed,
356 //unless TCGETS returns a "safe" termios?
357 if ((rv = ioctl(fd, TCGETS, &uti->st_cur)) != 0) {
358 break;
359 }
360
361 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
362 FLFMT "Speed set using TCSETS + BOTHER.\n", FL);
363 return spd_real;
364 }
365 if (rv != 0) {
366 fprintf(stderr, FLFMT "setspeed(BOTHER) ioctl failed: %s.\n",
367 FL, strerror(errno));
368 }
369
370#endif // BOTHER flag trick
371
372#if defined(__linux__) && (SEL_TTYBAUD==S_ALT2 || SEL_TTYBAUD==S_AUTO)
373//#pragma message("Warning : using deprecated ASYNC_SPD_CUST method as a fallback.")
374
375 /*
376 * Linux iX86 method of setting non-standard baud rates.
377 * This method is apparently deprecated, or at the very least not recommended
378 * and definitely not supported by all hardware because of TIOCSSERIAL.
379 *
380 * Works by manually setting the baud rate divisor and special flags.
381 *
382 */
383 if (uti->tioc_works && !spd_done) {
384 struct serial_struct ss_new;
385 /* Copy current settings to working copy */
386 ss_new = uti->ss_cur;
387
388 /* Now, mod the "current" settings */
389 ss_new.custom_divisor = ss_new.baud_base / spd;
390 spd_real = ss_new.baud_base / ss_new.custom_divisor;
391
392 /* Turn of other speed flags */
393 ss_new.flags &= ~ASYNC_SPD_MASK;
394 /*
395 * Turn on custom speed flags and low latency mode
396 */
397 ss_new.flags |= ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
398
399 /* And tell the kernel the new settings */
400 if (ioctl(fd, TIOCSSERIAL, &ss_new) < 0) {
401 fprintf(stderr,
402 FLFMT "setspeed(cust): ioctl failed: %s\n", FL, strerror(errno));
403 return 0;
404 }
405 //sucess : update current settings
406 uti->ss_cur = ss_new;
407
408 /*
409 * Set the baud rate and force speed to 38400 so that the
410 * custom baud rate stuff set above works
411 */
412 st_new.c_cflag &= ~CBAUD;
413 st_new.c_cflag |= B38400;
414 spd_done = 1;
415 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
416 FLFMT "Speed set using TIOCSSERIAL + ASYNC_SPD_CUST.\n", FL);
417 }
418#endif //deprecated ASYNC_SPD_CUST trick
419
420 /* "POSIXY" version of setting non-standard baud rates.
421 * This works at least for FreeBSD.
422 *
423 * POSIX states that it is unspecified what will happen with an
424 * unsupported baud rate. On FreeBSD, the baud rate defines match the
425 * baud rate (i.e., B9600 == 9600), and it attempts to set the baud
426 * rate you pass in, only complaining if the result is off by more
427 * than three percent.
428 *
429 * Unfortunately, I tried this on Mac OS X, and Mac OS X asserts that
430 * the baud rate must match one of the selections. A little research
431 * shows that not only does their iokit driver Serial class permit
432 * only the specific baud rates in the termios header, but at least
433 * the sample driver asserts the requested baud rate is 50 or more.
434 * I don't have the source code for the driver for the Keyspan device
435 * I can't tell if it would work if I modified the iokit serial class.
436 *
437 * use either spd_nearest (currently == nearest Bxxx value) as-is,
438 * or use spd (only if B9600 == 9600, etc.)
439 */
440
441 while (!spd_done) {
442 errno = 0;
443 int spd_nearest=0; //index of nearest std value
444 int32_t besterror=1000;
445
446 for (size_t i=0; i< ARRAY_SIZE(std_table); i++) {
447 int32_t test;
448 test = 1000 * ((long int)spd - std_table[i]) / spd;
449 test = (test >= 0)? test : -test;
450 if (test < besterror) {
451 besterror = test;
452 spd_nearest = i;
453 spd_real = std_table[i];
454 }
455 }
456
457 #if (B9600 == 9600) && (SEL_TTYBAUD==S_ALT3 || SEL_TTYBAUD==S_AUTO)
458 //try feeding the speed directly
459 if ( !cfsetispeed(&st_new, spd) &&
460 !cfsetospeed(&st_new, spd)) {
461 spd_real = spd;
462 spd_done = 1;
463 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
464 FLFMT "Speed set with cfset*speed(uint).\n", FL);
465
466 break;
467 }
468 fprintf(stderr,
469 "cfset*speed with direct speed failed: %s\n", strerror(errno));
470 #endif
471 if ( !cfsetispeed(&st_new, std_names[spd_nearest]) &&
472 !cfsetospeed(&st_new, std_names[spd_nearest])) {
473 //spd_real already ok
474 spd_done = 1;
475 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
476 FLFMT "Speed set with cfset*speed(B%u).\n",
477 FL, std_table[spd_nearest]);
478 break;
479 }
480 fprintf(stderr,
481 "cfset*speed with Bxxxx failed: %s\n", strerror(errno));
482 break;
483 } //while !spd_done for cfset*speed attempts
484
485 if (!spd_done) {
486 fprintf(stderr, "Error : all attempts at changing speed failed !\n");
487 return 0;
488 }
489
490#ifdef USE_TERMIOS2
491 //should never get here anyway
492 return 0;
493#else
494 errno = 0;
495 for (int retries=1; retries <=10; retries++) {
496 /* Apparently this sometimes failed with EINTR,
497 so we retry.
498 */
499
500 rv=tcsetattr(fd, TCSAFLUSH, &st_new);
501 if (rv == 0) {
502 break;
503 } else {
504 fprintf(stderr, FLFMT "Couldn't set baud rate....retry %d\n", FL, retries);
505 continue;
506 }
507 }
508
509 if (rv != 0) {
510 fprintf(stderr, FLFMT "Can't set baud rate to %u: %s.\n",
511 FL, spd, strerror(errno));
512 return 0;
513 }
514#endif
515 return spd_real;
516
517}
518/*
519 * Set speed/parity etc, return 0 if ok
520 */
521int diag_tty_setup(ttyp *tty_int, const struct diag_serial_settings *pset) {
522 int rv;
523 int fd;
524 struct unix_tty_int *uti = tty_int;
525 struct termios st_new;
526 unsigned int spd_real;
527 long int spd_err;
528
529 fd = uti->fd;
530
531 assert(fd != DL0D_INVALIDHANDLE);
532
533 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
534 FLFMT "setup: fd=%d, %ubps, %d bits, %d stop, parity %d\n",
535 FL, fd, pset->speed, pset->databits, pset->stopbits, pset->parflag);
536
537 /* Copy current settings to working copy */
538 st_new = uti->st_cur;
539 st_new.c_cflag &= ~CSIZE;
540 switch (pset->databits) {
541 case diag_databits_8:
542 st_new.c_cflag |= CS8;
543 break;
544 case diag_databits_7:
545 st_new.c_cflag |= CS7;
546 break;
547 case diag_databits_6:
548 st_new.c_cflag |= CS6;
549 break;
550 case diag_databits_5:
551 st_new.c_cflag |= CS5;
552 break;
553 default:
554 fprintf(stderr, FLFMT "bad bit setting used (%d)\n", FL, pset->databits);
555 return diag_iseterr(DIAG_ERR_GENERAL);
556 }
557 switch (pset->stopbits) {
558 case diag_stopbits_2:
559 st_new.c_cflag |= CSTOPB;
560 break;
561 case diag_stopbits_1:
562 st_new.c_cflag &= ~CSTOPB;
563 break;
564 default:
565 fprintf(stderr, FLFMT "bad stopbit setting used (%d)\n",
566 FL, pset->stopbits);
567 return diag_iseterr(DIAG_ERR_GENERAL);
568 }
569
570 switch (pset->parflag) {
571 case diag_par_e:
572 st_new.c_cflag |= PARENB;
573 st_new.c_cflag &= ~PARODD;
574 break;
575 case diag_par_o:
576 st_new.c_cflag |= (PARENB | PARODD);
577 break;
578 case diag_par_n:
579 st_new.c_cflag &= ~PARENB;
580 break;
581 default:
582 fprintf(stderr,
583 FLFMT "bad parity setting used (%d)\n", FL, pset->parflag);
584 return diag_iseterr(DIAG_ERR_GENERAL);
585 }
586
587 errno = 0;
588#ifdef USE_TERMIOS2
589 rv=ioctl(fd, TCSETS, &st_new);
590 rv |= ioctl(fd, TCGETS2, &uti->st2_cur);
591#else
592 rv=tcsetattr(fd, TCSAFLUSH, &st_new);
593#endif
594 if (rv != 0) {
595 fprintf(stderr,
596 FLFMT
597 "Can't set input flags (databits %d, stop bits %d, parity %d).\n"
598 "tcsetattr returned \"%s\".\n",
599 FL, pset->databits, pset->stopbits, pset->parflag,
600 strerror(errno));
601 return diag_iseterr(DIAG_ERR_GENERAL);
602 }
603
604 //update current settings
605 uti->st_cur = st_new;
606
607#if defined(_POSIX_TIMERS) || defined(__linux__)
608 //calculate write timeout for a single byte
609 //gross bits per byte: 1 start bit + count of data bits + count of stop bits + parity bit, if set
610 int gross_bits_per_byte = 1 + pset->databits + pset->stopbits + (pset->parflag == diag_par_n ? 0 : 1);
611 //single byte timeout to: (gross_bits_per_byte / (baudrate[1/s]/1000000[us/s]))[us];
612 uti->byte_write_timeout_us = (gross_bits_per_byte * 1000000ul / pset->speed);
613#endif
614
615 spd_real = _tty_setspeed(uti, pset->speed);
616 if (!spd_real) {
617 return diag_iseterr(DIAG_ERR_GENERAL);
618 }
619 //warn if actual speed is far off
620 spd_err = ((long int)spd_real - pset->speed)*100 / pset->speed;
621 spd_err = (spd_err >=0)? spd_err: -spd_err;
622 if (spd_err >= 5) {
623 fprintf(stderr, "Warning : speed off by >= 5%% !\n");
624 }
625
626 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
627 FLFMT "Speed set to %u, error~%ld%%\n",
628 FL, spd_real, spd_err);
629
630 return 0;
631} //diag_tty_setup
632
633/*
634 * Set/Clear DTR and RTS lines, as specified
635 */
636int diag_tty_control(ttyp *tty_int, unsigned int dtr, unsigned int rts) {
637 int flags; /* Current flag values. */
638 struct unix_tty_int *uti = tty_int;
639 int setflags = 0, clearflags = 0;
640
641 if (dtr) {
642 setflags = TIOCM_DTR;
643 } else {
644 clearflags = TIOCM_DTR;
645 }
646
647 if (rts) {
648 setflags = TIOCM_RTS;
649 } else {
650 clearflags = TIOCM_RTS;
651 }
652
653 errno = 0;
654 if (ioctl(uti->fd, TIOCMGET, &flags) < 0) {
655 fprintf(stderr,
656 FLFMT "open: Ioctl TIOCMGET failed %s\n", FL, strerror(errno));
657 return diag_iseterr(DIAG_ERR_GENERAL);
658 }
659 flags |= setflags;
660 flags &= ~clearflags;
661
662 if (ioctl(uti->fd, TIOCMSET, &flags) < 0) {
663 fprintf(stderr,
664 FLFMT "open: Ioctl TIOCMSET failed %s\n", FL, strerror(errno));
665 return diag_iseterr(DIAG_ERR_GENERAL);
666 }
667
668 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_TIMER, DIAG_DBGLEVEL_V,
669 FLFMT "%lu : DTR/RTS changed\n", FL, diag_os_getms());
670
671 return 0;
672}
673
674// diag_tty_write: return # of bytes written; <0 if error.
675// In addition, this calculates + enforces a write timeout based on the number of bytes.
676// But write timeouts should be very rare, and are considered an error
677ssize_t diag_tty_write(ttyp *tty_int, const void *buf, const size_t count) {
678 assert(count > 0);
679#if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO)
680 ssize_t rv;
681 struct unix_tty_int *uti = tty_int;
682 size_t n;
683 const uint8_t *p;
684 struct itimerspec it;
685
686 errno = 0;
687 p = (const uint8_t *)buf;
688 rv = 0;
689
690 //the timeout (the port is opened in blocking mode, and we don't want it to block indefinitely);
691 //the single byte timeout * count of bytes + 10ms (10 thousand microseconds; an arbitrary value)
692 long unsigned int timeout = uti->byte_write_timeout_us * count + 10000ul;
693 it.it_value.tv_sec = (time_t)(timeout / 1000000ul);
694 it.it_value.tv_nsec = (long)(timeout % 1000000ul)*1000l; //multiply by 1000 to get number of nanoseconds
695 //set interval to PT_REPEAT to make sure we don't block inside read()
696 it.it_interval.tv_sec=0;
697 it.it_interval.tv_nsec = PT_REPEAT * 1000;
698
699 uti->pt_expired=0;
700 //arm the timer
701 timer_settime(uti->timerid, 0, &it, NULL);
702
703 n=0;
704
705 while (n < count) {
706 if (uti->pt_expired) {
707 break;
708 }
709
710 rv = write(uti->fd, &p[n], count-n);
711 if (rv < 0) {
712 if (errno == EINTR) {
713 //not an error, just interrupted (probably a signal handler)
714 rv = 0;
715 errno = 0;
716 } else {
717 //real error:
718 break;
719 }
720 } else {
721 n += rv;
722 }
723 }
724
725 //disarm the timer in case it hasn't expired yet
726 it.it_value.tv_sec = it.it_value.tv_nsec = 0;
727 timer_settime(uti->timerid, 0, &it, NULL);
728
729 if (rv < 0) {
730 //errors other than EINTR
731 fprintf(stderr, FLFMT "write to fd %d returned %s.\n", FL, uti->fd, strerror(errno));
732 return diag_iseterr(DIAG_ERR_GENERAL);
733 }
734
735 //wait until the data is transmitted
736#ifdef USE_TERMIOS2
737 /* no exact equivalent ioctl for tcdrain,
738 but TCSBRK with arg !=0 is "treated like tcdrain(fd)" according
739 to info tty_ioctl */
740 if (ioctl(uti->fd, TCSBRK, 1) != 0) {
741 static int tcsb_warned=0;
742 if (!tcsb_warned) {
743 fprintf(stderr, "TCSBRK doesn't work!\n");
744 }
745 tcsb_warned=1;
746 }
747#else
748 tcdrain(uti->fd);
749#endif
750
751 return rv;
752} //_POSIX_TIMERS tty_write()
753
754#elif (SEL_TIMEOUT==S_LINUX || SEL_TIMEOUT==S_OTHER || SEL_TIMEOUT==S_AUTO)
755 /* No POSIX timers, this should be OK for everything else
756 * Technique: write loop, manually check timeout
757 */
758
759 ssize_t rv = DIAG_ERR_GENERAL;
760 ssize_t n;
761 size_t c = count;
762 struct unix_tty_int *uti = tty_int;
763 const uint8_t *p;
764 unsigned long long t1, t2;
765 long unsigned int timeout = uti->byte_write_timeout_us * count + 10000ul;
766
767 t1 = diag_os_gethrt();
768 p = (const uint8_t *)buf; /* For easy pointer I/O */
769 n = 0;
770 errno = 0;
771
772 while (c > 0) {
773 t2 = diag_os_hrtus(diag_os_gethrt() - t1);
774 if (t2 >= timeout) {
775 break;
776 }
777
778 rv = write(uti->fd, &p[n], c);
779 if (rv == -1 && errno == EINTR) {
780 rv = 0;
781 errno = 0;
782 continue;
783 }
784 if (rv < 0) {
785 fprintf(stderr, FLFMT "write error: %s.\n", FL, strerror(errno));
786 return diag_iseterr(DIAG_ERR_GENERAL);
787 }
788 c -= rv;
789 n += rv;
790 }
791
792 if (n > 0 || rv >= 0) {
793 //wait until the data is transmitted
794#ifdef USE_TERMIOS2
795 /* no exact equivalent ioctl for tcdrain, but
796 "TCSBRK : [...] treat tcsendbreak(fd,arg) with nonzero arg like tcdrain(fd)."
797 */
798 ioctl(uti->fd, TCSBRK, 1);
799#else
800 tcdrain(uti->fd);
801#endif
802 return n;
803 }
804
805 fprintf(stderr, FLFMT "write to fd %d returned %s.\n",
806 FL, uti->fd, strerror(errno));
807
808 /* Unspecific Error */
809 return diag_iseterr(DIAG_ERR_GENERAL);
810} //S_OTHER || S_LINUX write implem
811#else
812 #error Fell in the cracks of implementation selectors !
813#endif //tty_write() implementations
814
815
816ssize_t diag_tty_read(ttyp *tty_int, void *buf, size_t count, unsigned int timeout) {
817 assert((count > 0) && (timeout > 0) && (timeout < MAXTIMEOUT));
818#if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO)
819 ssize_t rv;
820 size_t n;
821 int expired;
822 uint8_t *p;
823 struct unix_tty_int *uti = tty_int;
824
825 struct itimerspec it;
826
827 //the timeout
828 it.it_value.tv_sec = timeout / 1000;
829 it.it_value.tv_nsec = (timeout % 1000) * 1000000;
830 //set interval to PT_REPEAT to make sure we don't block inside read()
831 it.it_interval.tv_sec=0;
832 it.it_interval.tv_nsec = PT_REPEAT * 1000;
833
834 uti->pt_expired=0;
835 expired=0;
836
837 //arm the timer
838 timer_settime(uti->timerid, 0, &it, NULL);
839
840 n = 0;
841 p = (uint8_t *)buf;
842 errno = 0;
843
844 while (n < count) {
845 if (uti->pt_expired) {
846 expired=1;
847 break;
848 }
849
850 rv = read(uti->fd, &p[n], count-n);
851 if (rv < 0) {
852 if (errno == EINTR) {
853 //not an error, just an interrupted syscall
854 rv = 0;
855 errno = 0;
856 } else {
857 //real error:
858 break;
859 }
860 } else {
861 n += rv;
862 }
863 }
864
865 //always disarm the timer
866 it.it_value.tv_sec = it.it_value.tv_nsec = 0;
867 timer_settime(uti->timerid, 0, &it, NULL);
868
869 //if anything has been read, then return the number of read bytes; return timeout error otherwise
870 if (rv >= 0) {
871 if (n > 0) {
872 return n;
873 }
874 if (expired) {
875 return DIAG_ERR_TIMEOUT; // without diag_iseterr() !
876 }
877 }
878
879 //errors other than EINTR
880 fprintf(stderr, FLFMT "read on fd %d returned %s.\n", FL, uti->fd, strerror(errno));
881
882 //Unspecified error
883 return diag_iseterr(DIAG_ERR_GENERAL);
884} //POSIX read implem
885
886#elif (SEL_TIMEOUT==S_OTHER || SEL_TIMEOUT == S_AUTO)
887 //no posix timers and it's not linux
888 //Loop with { select() with a timeout;
889 // read() ; manually check timeout}
890
891 ssize_t rv = DIAG_ERR_GENERAL;
892 ssize_t n;
893 uint8_t *p;
894 unsigned long long tstart, incr, tdone, tdone_us;
895 struct unix_tty_int *uti = tty_int;
896
897 int expired = 0;
898 tstart=diag_os_gethrt();
899 incr = timeout * 1000; //us
900
901 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_TIMER, DIAG_DBGLEVEL_V,
902 "timeout=%u, start=%llu, delta=%llu\n", timeout, tstart, incr);
903
904 errno = 0;
905 p = (uint8_t *)buf; /* For easy pointer I/O */
906 n = 0;
907
908 while (count > 0 && expired == 0) {
909 //select() loop to ensure read() won't block:
910 while ( !expired ) {
911 fd_set set;
912 struct timeval tv;
913 unsigned long long rmn;
914
915 tdone = diag_os_gethrt() - tstart;
916 tdone_us = diag_os_hrtus(tdone);
917
918 if (tdone_us >= incr) {
919 expired = 1;
920 rv=0;
921 goto finished;
922 }
923 rmn = timeout*1000 - tdone_us; //remaining before timeout
924
925 FD_ZERO(&set);
926 FD_SET(uti->fd, &set);
927
928 tv.tv_sec = rmn / (1000*1000);
929 tv.tv_usec = rmn % (1000*1000);
930
931 rv = select( uti->fd + 1, &set, NULL, NULL, &tv );
932 // 4 possibilities here:
933 // EINTR => retry
934 // FD ready => break
935 // timed out => retry (will DIAG_ERR_TIMEOUT)
936 // other errors => diag_iseterr
937 if ((rv < 0) && (errno == EINTR)) {
938 rv=0;
939 errno=0;
940 continue;
941 }
942 if (FD_ISSET(uti->fd, &set)) {
943 //fd is ready:
944 break;
945 }
946 if (rv < 0) {
947 fprintf(stderr, FLFMT "select() error: %s.\n", FL, strerror(errno));
948 return diag_iseterr(DIAG_ERR_GENERAL);
949 }
950 } //select loop
951
952 rv = read(uti->fd, &p[n], count);
953
954 if ((rv < 0) && (rv == EINTR)) {
955 rv=0;
956 errno=0;
957 continue;
958 }
959
960 if (rv<=0) {
961 fprintf(stderr, FLFMT "read() says %ld: %s.\n", FL, (long) rv, strerror(errno));
962 break;
963 }
964
965 count -= rv;
966 n += rv;
967 } //total read loop
968finished:
969 if (rv >= 0) {
970 if (n > 0) {
971 return n;
972 } else if (expired) {
973 return DIAG_ERR_TIMEOUT;
974 }
975 }
976
977 fprintf(stderr, FLFMT "read() returned %s.\n",
978 FL, strerror(errno));
979
980 /* Unspecified Error */
981 return diag_iseterr(DIAG_ERR_GENERAL);
982} //S_OTHER read implem
983
984#elif defined(__linux__) && (SEL_TIMEOUT==S_LINUX || SEL_TIMEOUT==S_AUTO)
985
986/*
987 * We have to read to loop since we've cleared SA_RESTART.
988 *
989 * This implementation uses /dev/rtc to time out, but seems flawed : it
990 * also relies on select() to guarantee that (count) bytes are available.
991 *
992 * Also, it calls select() very very often (why is tv={0} ?)
993 */
994
995 struct timeval tv;
996 unsigned int time;
997 int rv,fd,retval;
998 unsigned long data;
999 struct unix_tty_int *uti = tty_int;;
1000
1001 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V,
1002 FLFMT "Entered diag_tty_read with count=%u, timeout=%ums\n",
1003 FL, (unsigned int) count, timeout);
1004
1005 errno = 0;
1006 time = 0;
1007
1008 tv.tv_sec = 0;
1009 tv.tv_usec = 0;
1010
1011 timeout = (int)((unsigned long) timeout * 2048/1000); //watch for overflow !
1012
1013 fd = open ("/dev/rtc", O_RDONLY);
1014 if (fd <=0) {
1015 fprintf(stderr, FLFMT "diag_tty_read: error opening /dev/rtc !\n", FL);
1016 perror("\t");
1017 return diag_iseterr(DIAG_ERR_GENERAL);
1018 }
1019
1020 /* Read periodic IRQ rate */
1021 retval = ioctl(fd, RTC_IRQP_READ, &data);
1022
1023 if (retval != 2048) {
1024 ioctl(fd, RTC_IRQP_SET, 2048);
1025 }
1026
1027 /* Enable periodic interrupts */
1028 ioctl(fd, RTC_PIE_ON, 0);
1029
1030 read(fd, &data, sizeof(unsigned long));
1031 data >>= 8;
1032 time+=data;
1033
1034 while ( 1 ) {
1035 fd_set set;
1036
1037 FD_ZERO(&set);
1038 FD_SET(uti->fd, &set);
1039
1040 rv = select ( uti->fd + 1, &set, NULL, NULL, &tv );
1041
1042 if ( rv > 0 ) {
1043 break;
1044 }
1045
1046 if (errno != 0 && errno != EINTR) {
1047 break;
1048 }
1049 errno = 0;
1050
1051 read(fd, &data, sizeof(unsigned long));
1052 data >>= 8;
1053 time+=data;
1054 if (time>=timeout) {
1055 break;
1056 }
1057 }
1058
1059 /* Disable periodic interrupts */
1060 ioctl(fd, RTC_PIE_OFF, 0);
1061 close(fd);
1062
1063
1064 if (time>=timeout) {
1065 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
1066 FLFMT "timed out: %ums\n",FL,timeout*1000/2048);
1067 }
1068
1069 switch (rv) {
1070 case 0:
1071 /* Timeout */
1072 //this doesn't require a diag_iseterr() call, as is the case when diag_tty_read
1073 //is called to flush
1074 return DIAG_ERR_TIMEOUT;
1075 case 1:
1076 /* Ready for read */
1077 rv = 0;
1078 /*
1079 * XXX Won't you hang here if "count" bytes don't arrive?
1080 * XXX Yes, possibly !
1081 */
1082 rv = read(uti->fd, buf, count);
1083 if (rv <= 0) {
1084 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V,
1085 "read() returned %d?", rv);
1086 }
1087 return rv;
1088
1089 default:
1090 fprintf(stderr, FLFMT "select on fd %d returned %s.\n",
1091 FL, uti->fd, strerror(errno));
1092
1093 /* Unspecific Error */
1094 return diag_iseterr(DIAG_ERR_GENERAL);
1095 }
1096} //S_LINUX read implem
1097
1098#else
1099 #error Fell in the cracks of implementation selectors !
1100#endif //_tty_read() implementations
1101
1102
1103/*
1104 * POSIX serial I/O input flush +
1105 * diag_tty_read with IFLUSH_TIMEOUT.
1106 * Ret 0 if ok
1107 */
1108int diag_tty_iflush(ttyp *tty_int) {
1109 uint8_t buf[MAXRBUF];
1110 int rv;
1111 struct unix_tty_int *uti = tty_int;
1112
1113 errno = 0;
1114
1115#ifdef USE_TERMIOS2
1116 rv=ioctl(uti->fd, TCFLSH, TCIFLUSH);
1117#else
1118 rv=tcflush(uti->fd, TCIFLUSH);
1119#endif // USE_TERMIOS2
1120 if ( rv != 0) {
1121 fprintf(stderr, FLFMT "TCIFLUSH on fd %d returned %s.\n",
1122 FL, uti->fd, strerror(errno));
1123 }
1124
1125 /* Read any old data hanging about on the port */
1126 rv = diag_tty_read(uti, buf, sizeof(buf), IFLUSH_TIMEOUT);
1127 if (rv > 0) {
1128 //not dumping data : could flood screen
1129 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_DATA, DIAG_DBGLEVEL_V,
1130 FLFMT "tty_iflush: >=%d junk bytes discarded: 0x%X...\n",
1131 FL, rv, buf[0]);
1132 }
1133
1134 return 0;
1135} //diag_tty_iflush
1136
1137
1138
1139// ideally use TIOCSBRK, if defined (probably in sys/ioctl.h)
1140int diag_tty_break(ttyp *tty_int, const unsigned int ms) {
1141#ifdef TIOCSBRK
1142// TIOCSBRK: set TX break until TIOCCBRK. Ideal for our use but not in POSIX.
1143/*
1144 * This one returns right after clearing the break. This is more generic and
1145 * can be used to bit-bang a 5bps byte.
1146 */
1147 struct unix_tty_int *uti = tty_int;
1148#ifdef USE_TERMIOS2
1149 /* no exact equivalent ioctl for tcdrain, but
1150 "TCSBRK : [...] treat tcsendbreak(fd,arg) with nonzero arg like tcdrain(fd)."
1151 */
1152 ioctl(uti->fd, TCSBRK, 1);
1153#else
1154 if (tcdrain(uti->fd)) {
1155 fprintf(stderr, FLFMT "tcdrain returned %s.\n",
1156 FL, strerror(errno));
1157 return diag_iseterr(DIAG_ERR_GENERAL);
1158 }
1159#endif
1160
1161 if (ioctl(uti->fd, TIOCSBRK, 0) < 0) {
1162 fprintf(stderr,
1163 FLFMT "open: Ioctl TIOCSBRK failed %s\n", FL, strerror(errno));
1164 return diag_iseterr(DIAG_ERR_GENERAL);
1165 }
1166
1167 diag_os_millisleep(ms);
1168
1169 if (ioctl(uti->fd, TIOCCBRK, 0) < 0) {
1170 fprintf(stderr,
1171 FLFMT "open: Ioctl TIOCCBRK failed %s\n", FL, strerror(errno));
1172 return diag_iseterr(DIAG_ERR_GENERAL);
1173 }
1174
1175 return 0;
1176
1177#else
1178#warning ******* Dont know how to implement diag_tty_break() on your OS !
1179#warning ******* Compiling diag_tty_break with a fixed 25ms setbreak !
1180#warning ******* DUMB interfaces may not work properly !!
1181 if (ms<25) {
1182 return 0;
1183 }
1184
1185 return diag_tty_fastbreak(uti, ms);
1186#endif //if .. for diag_tty_break
1187} //diag_tty_break
1188
1189
1190
1191/*
1192 * diag_tty_fastbreak
1193 * fixed 25ms (1 byte 0x00 @ 360bps !) break and returns [ms] after starting the break.
1194 * Assumes half-duplex interface of course;
1195 * it also sets 10.4kbps 8N1, hardcoded. XXX find a way to make this neater..
1196 * we'll probably have to add a ->pset member to diag_l0_device to store the
1197 * "desired" setting. And use that to call diag_tty_setup to restore settings...
1198 */
1199int diag_tty_fastbreak(ttyp *tty_int, const unsigned int ms) {
1200 struct unix_tty_int *uti=tty_int;
1201 uint8_t cbuf;
1202 unsigned long long tv1,tv2,tvdiff;
1203 struct diag_serial_settings set;
1204 unsigned int msremain;
1205
1206 if (ms < 25) {
1207 return diag_iseterr(DIAG_ERR_TIMEOUT);
1208 }
1209
1210 /* Set baud rate etc to 360 baud, 8, N, 1 */
1211 set.speed = 360;
1212 set.databits = diag_databits_8;
1213 set.stopbits = diag_stopbits_1;
1214 set.parflag = diag_par_n;
1215
1216 if (diag_tty_setup(uti, &set)) {
1217 fprintf(stderr, FLFMT "Could not set 360bps for fastbreak !\n", FL);
1218 return diag_iseterr(DIAG_ERR_GENERAL);
1219 }
1220
1221 tv1 = diag_os_gethrt();
1222 /* Send a 0x00 byte message */
1223 diag_tty_write(uti, "", 1);
1224 //Alternate method ; we can write() ourselves and then tcdrain() to make
1225 //sure data is sent ?
1226
1227 /*
1228 * And read back the single byte echo, which shows TX completes
1229 */
1230 if (diag_tty_read(uti, &cbuf, 1, 1000) != 1) {
1231 fprintf(stderr, FLFMT "tty_fastbreak: echo read error\n", FL);
1232 return diag_iseterr(DIAG_ERR_GENERAL);
1233 }
1234
1235 //we probably have a few ms left;
1236 //restore 10400bps:
1237 set.speed = 10400;
1238 if (diag_tty_setup(uti, &set)) {
1239 fprintf(stderr, FLFMT "Could not restore settings after fastbreak!\n", FL);
1240 return diag_iseterr(DIAG_ERR_GENERAL);
1241 }
1242
1243 /* Now wait the requested number of ms */
1244 tv2=diag_os_gethrt();
1245 tvdiff = diag_os_hrtus(tv2 - tv1); //us
1246
1247 if (tvdiff >= (ms * 1000)) {
1248 return 0; // already finished
1249 }
1250
1251 msremain = ms - (tvdiff / 1000);
1252
1253 diag_os_millisleep(msremain);
1254
1255 tv2=diag_os_gethrt();
1256 tvdiff = diag_os_hrtus(tv2 - tv1); //us
1257
1258 //XXX this message may need to be removed if timing is impaired
1259 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_TIMER, DIAG_DBGLEVEL_V,
1260 FLFMT "Fast break finished : tWUP=%llu\n", FL, tvdiff);
1261
1262 return 0;
1263}
1264
1265//ret true if pname is a tty
1266static bool test_ttyness(const char *pname) {
1267 int testfd = -1; // file descriptor for tested device files
1268 bool yes=0;
1269
1270 testfd = open(pname, O_RDWR | O_NOCTTY | O_NDELAY);
1271 if (testfd != -1) {
1272 if (isatty(testfd)) {
1273 yes = 1;
1274 }
1275 close(testfd);
1276 }
1277 return yes;
1278}
1279
1280/* To find available ports, iterate in /dev/ and /dev/usb/
1281 * to find & test possible port names.
1282 * Adapted from FreeSSM :
1283 * https://github.com/Comer352L/FreeSSM
1284 */
1285char **diag_tty_getportlist(int *numports) {
1286 char ffn[256] = ""; // full filename incl. path
1287 const char *devroot="/dev/";
1288 const char *devusbroot="/dev/usb";
1289 DIR *dp = NULL;
1290 struct dirent *fp = NULL;
1291 char **portlist = NULL;
1292 int elems; //temp number of ports
1293
1294 assert(numports != NULL);
1295 *numports = 0;
1296 elems = 0;
1297
1298 /* 1: iterate in /dev/ */
1299 dp = opendir (devroot);
1300 if (dp != NULL) {
1301 while (1) {
1302 fp = readdir (dp); // get next file in directory
1303 if (fp == NULL) {
1304 break;
1305 }
1306 if ((!strncmp(fp->d_name,"ttyS",4)) ||
1307 (!strncmp(fp->d_name,"ttyUSB",6)) ||
1308 (!strncmp(fp->d_name,"ttyACM",6))) {
1309 // CONSTRUCT FULL FILENAME:
1310 strcpy(ffn, devroot);
1311 strncat(ffn, fp->d_name, ARRAY_SIZE(ffn) - strlen(devroot) - 1);
1312
1313 if (!test_ttyness(ffn)) {
1314 continue;
1315 }
1316
1317 char **templist = strlist_add(portlist, ffn, elems);
1318 if (!templist) {
1319 strlist_free(portlist, elems);
1320 return diag_pseterr(DIAG_ERR_NOMEM);
1321 }
1322 portlist = templist;
1323 elems++;
1324 }
1325 }
1326 closedir (dp);
1327 }
1328
1329 /* iterate in /dev/usb */
1330 dp = opendir (devusbroot);
1331 if (dp != NULL) {
1332 while (1) {
1333 fp = readdir (dp); // get next file in directory
1334 if (fp == NULL) {
1335 break;
1336 }
1337 if (!strncmp(fp->d_name,"ttyUSB",6)) {
1338 // CONSTRUCT FULL FILENAME:
1339 strcpy(ffn, devusbroot);
1340 strncat(ffn, fp->d_name, ARRAY_SIZE(ffn) - strlen(devusbroot) - 1);
1341
1342 if (!test_ttyness(ffn)) {
1343 continue;
1344 }
1345
1346 char **templist = strlist_add(portlist, ffn, elems);
1347 if (!templist) {
1348 strlist_free(portlist, elems);
1349 return diag_pseterr(DIAG_ERR_NOMEM);
1350 }
1351 portlist = templist;
1352 elems++;
1353 }
1354 } //while
1355 closedir (dp);
1356 }
1357
1358 *numports = elems;
1359 return portlist;
1360}
1361