//diag_l0_elm_close : free pdl0d->dl0 (dev), call diag_tty_close. static int diag_l0_elm_close(struct diag_l0_device **pdl0d) { assert(pdl0d != NULL); assert(*pdl0d != NULL); uint8_t buf[]="ATPC\x0D"; elm_sendcmd(*pdl0d, buf, 5, 500, NULL); //close protocol. So clean ! if (pdl0d && *pdl0d) { struct diag_l0_device *dl0d = *pdl0d; struct diag_l0_elm_device *dev = (struct diag_l0_elm_device *)dl0d->l0_int; /* If debugging, print to stderr */ if (diag_l0_debug & DIAG_DEBUG_CLOSE) fprintf(stderr, FLFMT "link %p closing\n", FL, (void *) dl0d); if (dev) free(dev); (void) diag_tty_close(dl0d); diag_l0_del(*pdl0d); } return 0; }
static void br_close(struct diag_l0_device *dl0d) { if (!dl0d) { return; } struct br_device *dev = dl0d->l0_int; DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_CLOSE, DIAG_DBGLEVEL_V, FLFMT "link %p closing\n", FL, (void *)dl0d); diag_tty_close(dev->tty_int); dev->tty_int = NULL; dl0d->opened = 0; return; }
static int diag_l0_br_close(struct diag_l0_device **pdl0d) { if (pdl0d && *pdl0d) { struct diag_l0_device *dl0d = *pdl0d; struct diag_l0_br_device *dev = (struct diag_l0_br_device *)dl0d->l0_int; if (diag_l0_debug & DIAG_DEBUG_CLOSE) fprintf(stderr, FLFMT "link %p closing\n", FL, (void *)dl0d); if (dev) free(dev); (void) diag_tty_close(dl0d); diag_l0_del(dl0d); } return 0; }
static void diag_l0_muleng_close(struct diag_l0_device *dl0d) { if (!dl0d) return; struct diag_l0_muleng_device *dev = (struct diag_l0_muleng_device *)dl0d->l0_int; if (diag_l0_debug & DIAG_DEBUG_CLOSE) fprintf(stderr, FLFMT "link %p closing\n", FL, (void *)dl0d); if (dev) free(dev); diag_tty_close(dl0d); diag_l0_del(dl0d); return; }
//diag_tty_open : open specified port for L0 int diag_tty_open(struct diag_l0_device *dl0d, const char *portname) { int rv; struct tty_int *wti; size_t n = strlen(portname) + 1; COMMTIMEOUTS devtimeouts; if (!dl0d) return diag_iseterr(DIAG_ERR_GENERAL); if ((rv=diag_calloc(&wti,1))) { return diag_iseterr(rv); } dl0d->tty_int = wti; wti->fd = INVALID_HANDLE_VALUE; //allocate space for portname name if ((rv=diag_malloc(&wti->name, n))) { free(dl0d->tty_int); return diag_iseterr(rv); } //Now, in case of errors we can call diag_tty_close() on the dl0d since its members are alloc'ed strncpy(wti->name, portname, n); wti->fd=CreateFile(portname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL); //File hande is created as non-overlapped. This may change eventually. if (wti->fd != INVALID_HANDLE_VALUE) { if (diag_l0_debug & DIAG_DEBUG_OPEN) fprintf(stderr, FLFMT "Device %s opened, fd %p\n", FL, wti->name, wti->fd); } else { fprintf(stderr, FLFMT "Open of device interface \"%s\" failed: %s\n", FL, wti->name, diag_os_geterr(0)); fprintf(stderr, FLFMT "(Make sure the device specified corresponds to the\n", FL ); fprintf(stderr, FLFMT "serial device your interface is connected to.\n", FL); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } //purge & abort everything. PurgeComm(wti->fd,PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); //as opposed to the unix diag_tty.c ; this one doesn't save previous commstate. The next program to use the COM port //will need to deal with it... //We will load the DCB with the current comm state. This way we only need to call GetCommState once during a session //and the DCB should contain coherent initial values if (! GetCommState(wti->fd, &wti->dcb)) { fprintf(stderr, FLFMT "Could not get comm state: %s\n",FL, diag_os_geterr(0)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } //Finally set COMMTIMEOUTS to reasonable values (all in ms) ? devtimeouts.ReadIntervalTimeout=30; //i.e. more than 30ms between received bytes devtimeouts.ReadTotalTimeoutMultiplier=5; //timeout per requested byte devtimeouts.ReadTotalTimeoutConstant=20; // (constant + multiplier*numbytes) = total timeout on read(buf, numbytes) devtimeouts.WriteTotalTimeoutMultiplier=0; //probably useless as all flow control will be disabled ?? devtimeouts.WriteTotalTimeoutConstant=0; if (! SetCommTimeouts(wti->fd,&devtimeouts)) { fprintf(stderr, FLFMT "Could not set comm timeouts: %s\n",FL, diag_os_geterr(0)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } return 0; } //diag_tty_open
int diag_tty_open(struct diag_l0_device **ppdl0d, const char *subinterface, const struct diag_l0 *dl0, void *dl0_handle) { int rv; struct diag_ttystate *dt; struct diag_l0_device *dl0d; char *endptr; int iInterface; const char *tty_template ="/dev/obdII%d"; if (rv=diag_calloc(&dl0d, 1)) //free'd in diag_tty_close return diag_iseterr(rv); dl0d->fd = -1; dl0d->dl0_handle = dl0_handle; dl0d->dl0 = dl0; if ((rv=diag_calloc(&dl0d->ttystate, 1))) { free(dl0d); return diag_iseterr(rv); } *ppdl0d = dl0d; /* * XXX this should probably be removed... "historical compatibility" with what ?? Who ? * For historical compatibility, if the subinterface decodes cleanly * as an integer we will write it into a string to get the name. * You can create a symlink to "/dev/obdII<NUMBER>" if you want to, * or just set the subinterface to a valid device name. */ iInterface = strtol(subinterface, &endptr, 10); if (*endptr == 0) { /* Entire string is a valid number: Provide compatibility. */ size_t n = strlen(tty_template) + 32; printf("Warning : using deprecated /dev/obdII<x> subinterface definition.\n"); printf("Support for this will be discontinued shortly...\n"); if ((rv=diag_malloc(&dl0d->name, n))) { (void)diag_tty_close(ppdl0d);; return diag_iseterr(rv); } (void)snprintf(dl0d->name, n, tty_template, iInterface); } else { size_t n = strlen(subinterface) + 1; if ((rv=diag_malloc(&dl0d->name, n))) { (void)diag_tty_close(ppdl0d);; return diag_iseterr(rv); } strncpy(dl0d->name, subinterface, n); } errno = 0; #if defined(__linux__) && (TRY_POSIX == 0) dl0d->fd = open(dl0d->name, O_RDWR); #else /* +* For POSIX behavior: Open serial device non-blocking to avoid +* modem control issues, then set to blocking. */ /* CODE BLOCK */ { int fl; dl0d->fd = open(dl0d->name, O_RDWR | O_NONBLOCK); if (dl0d->fd > 0) { errno = 0; if ((fl = fcntl(dl0d->fd, F_GETFL, 0)) < 0) { fprintf(stderr, FLFMT "Can't get flags with fcntl on fd %d: %s.\n", FL, dl0d->fd, strerror(errno)); (void)diag_tty_close(ppdl0d);; return diag_iseterr(DIAG_ERR_GENERAL); } fl &= ~O_NONBLOCK; errno = 0; if (fcntl(dl0d->fd, F_SETFL, fl) < 0) { fprintf(stderr, FLFMT "Can't set flags with fcntl on fd %d: %s.\n", FL, dl0d->fd, strerror(errno)); (void)diag_tty_close(ppdl0d); return diag_iseterr(DIAG_ERR_GENERAL); } } } #endif if (dl0d->fd >= 0) { if (diag_l0_debug & DIAG_DEBUG_OPEN) fprintf(stderr, FLFMT "Device %s opened, fd %d\n", FL, dl0d->name, dl0d->fd); } else { fprintf(stderr, FLFMT "Open of device interface \"%s\" failed: %s\n", FL, dl0d->name, strerror(errno)); fprintf(stderr, FLFMT "(Make sure the device specified corresponds to the\n", FL ); fprintf(stderr, FLFMT "serial device your interface is connected to.\n", FL); (void)diag_tty_close(ppdl0d); return diag_iseterr(DIAG_ERR_GENERAL); } dt = dl0d->ttystate; /* * Save original settings so can reset * device on close - we also set "current" settings to * those we just read aswell */ #if defined(__linux__) && (TRY_POSIX == 0) if (ioctl(dl0d->fd, TIOCGSERIAL, &dt->dt_osinfo) < 0) { fprintf(stderr, FLFMT "open: Ioctl TIOCGSERIAL failed %d\n", FL, errno); (void)diag_tty_close(ppdl0d); return diag_iseterr(DIAG_ERR_GENERAL); } dt->dt_sinfo = dt->dt_osinfo; #endif if (ioctl(dl0d->fd, TIOCMGET, &dt->dt_modemflags) < 0) { fprintf(stderr, FLFMT "open: Ioctl TIOCMGET failed: %s\n", FL, strerror(errno)); (void)diag_tty_close(ppdl0d); return diag_iseterr(DIAG_ERR_GENERAL); } if (tcgetattr(dl0d->fd, &dt->dt_otinfo) < 0) { fprintf(stderr, FLFMT "open: tcgetattr failed %s\n", FL, strerror(errno)); (void)diag_tty_close(ppdl0d); return diag_iseterr(DIAG_ERR_GENERAL); } dt->dt_tinfo = dt->dt_otinfo; return 0; }
int diag_tty_open(struct diag_l0_device *dl0d, const char *portname) { int rv; struct unix_tty_int *uti; #if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO) struct sigevent to_sigev; struct sigaction sa; clockid_t timeout_clkid; #endif assert(dl0d != NULL); if (dl0d->tty_int) return diag_iseterr(DIAG_ERR_GENERAL); if ((rv=diag_calloc(&uti,1))) { return diag_iseterr(rv); } #if defined(_POSIX_TIMERS) && (SEL_TIMEOUT==S_POSIX || SEL_TIMEOUT==S_AUTO) //set-up the r/w timeouts clock - here we just create it; it will be armed when needed #ifdef _POSIX_MONOTONIC_CLOCK timeout_clkid = CLOCK_MONOTONIC; #else timeout_clkid = CLOCK_REALTIME; #endif // _POSIX_MONOTONIC_CLOCK sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = diag_tty_rw_timeout_handler; sigemptyset(&sa.sa_mask); if(sigaction(SIGUSR1, &sa, NULL) != 0) { fprintf(stderr, FLFMT "Could not set-up action for timeout timer... report this\n", FL); free(uti); return diag_iseterr(DIAG_ERR_GENERAL); } to_sigev.sigev_notify = SIGEV_SIGNAL; to_sigev.sigev_signo = SIGUSR1; to_sigev.sigev_value.sival_ptr = uti; if(timer_create(timeout_clkid, &to_sigev, &uti->timerid) != 0) { fprintf(stderr, FLFMT "Could not create timeout timer... report this\n", FL); free(uti); return diag_iseterr(DIAG_ERR_GENERAL); } #endif uti->fd = DL0D_INVALIDHANDLE; size_t n = strlen(portname) + 1; if ((rv=diag_malloc(&uti->name, n))) { free(uti); return diag_iseterr(rv); } strncpy(uti->name, portname, n); dl0d->tty_int = uti; //past this point, we can call diag_tty_close(dl0d) to abort in case of errors errno = 0; #if defined(O_NONBLOCK) && (SEL_TTYOPEN==S_ALT1 || SEL_TTYOPEN==S_AUTO) /* * For POSIX behavior: Open serial device non-blocking to avoid * modem control issues, then set to blocking. */ { int fl; uti->fd = open(uti->name, O_RDWR | O_NONBLOCK); if (uti->fd > 0) { errno = 0; if ((fl = fcntl(uti->fd, F_GETFL, 0)) < 0) { fprintf(stderr, FLFMT "Can't get flags with fcntl on fd %d: %s.\n", FL, uti->fd, strerror(errno)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } fl &= ~O_NONBLOCK; errno = 0; if (fcntl(uti->fd, F_SETFL, fl) < 0) { fprintf(stderr, FLFMT "Can't set flags with fcntl on fd %d: %s.\n", FL, uti->fd, strerror(errno)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } } } #else #ifndef O_NONBLOCK #warning No O_NONBLOCK on your system ?! Please report this #endif uti->fd = open(uti->name, O_RDWR); #endif // O_NONBLOCK if (uti->fd >= 0) { if (diag_l0_debug & DIAG_DEBUG_OPEN) fprintf(stderr, FLFMT "Device %s opened, fd %d\n", FL, uti->name, uti->fd); } else { fprintf(stderr, FLFMT "Could not open \"%s\" : %s. " "Make sure the device specified corresponds to the " "serial device your interface is connected to.\n", FL, uti->name, strerror(errno)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } /* * Save original settings so can reset * device on close - we also set "current" settings to * those we just read aswell */ #if defined(__linux__) if (ioctl(uti->fd, TIOCGSERIAL, &uti->ss_orig) < 0) { fprintf(stderr, FLFMT "open: TIOCGSERIAL failed: %s\n", FL, strerror(errno)); uti->tioc_works = 0; } else { uti->ss_cur = uti->ss_orig; uti->tioc_works = 1; } #endif if (ioctl(uti->fd, TIOCMGET, &uti->modemflags) < 0) { fprintf(stderr, FLFMT "open: TIOCMGET failed: %s\n", FL, strerror(errno)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } #ifdef USE_TERMIOS2 rv = ioctl(uti->fd, TCGETS, &uti->st_orig); #else rv = tcgetattr(uti->fd, &uti->st_orig); #endif if (rv != 0) { fprintf(stderr, FLFMT "open: could not get orig settings: %s\n", FL, strerror(errno)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } //and set common flags uti->st_cur = uti->st_orig; /* "stty raw"-like iflag settings: */ /* Clear a bunch of un-needed flags */ uti->st_cur.c_iflag &= ~ (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | IMAXBEL); #ifdef __linux__ uti->st_cur.c_iflag &= ~(IUCLC); /* non-posix; disable ucase/lcase conversion */ #endif uti->st_cur.c_oflag &= ~(OPOST); //disable impl-defined output processing /* Disable canonical input and keyboard signals. +* There is no need to also clear the many ECHOXX flags, both because +* many systems have non-POSIX flags and also because the ECHO +* flags don't don't matter when ICANON is clear. */ /* CJH: However, taking 'man termios' at its word, the ECHO flag is *not* affected by ICANON, and it seems we do need to clear it */ uti->st_cur.c_lflag &= ~( ICANON | ISIG | ECHO | IEXTEN); uti->st_cur.c_cflag &= ~( CRTSCTS ); //non-posix; disables hardware flow ctl uti->st_cur.c_cflag |= (CLOCAL | CREAD); //ignore modem control lines; enable read uti->st_cur.c_cc[VMIN] = 1; //Minimum # of bytes before read() returns (default: 0!!!) //and update termios with new flags. #ifdef USE_TERMIOS2 rv = ioctl(uti->fd, TCSETS, &uti->st_cur); rv |= ioctl(uti->fd, TCGETS2, &uti->st2_cur); #else rv=tcsetattr(uti->fd, TCSAFLUSH, &uti->st_cur); #endif if (rv != 0) { fprintf(stderr, FLFMT "open: can't set input flags: %s.\n", FL, strerror(errno)); diag_tty_close(dl0d); return diag_iseterr(DIAG_ERR_GENERAL); } //arbitrarily set the single byte write timeout to 1ms uti->byte_write_timeout_us = 1000ul; return 0; }