| 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. |
| 51 | static 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 | |
| 58 | ttyp *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. */ |
| 250 | void 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 | */ |
| 299 | static 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 | */ |
| 521 | int 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 | */ |
| 636 | int 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 |
| 677 | ssize_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 | |
| 816 | ssize_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 |
| 968 | finished: |
| 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 | */ |
| 1108 | int 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) |
| 1140 | int 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 | */ |
| 1199 | int 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 |
| 1266 | static 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 | */ |
| 1285 | char **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 | |