diff --git a/capture_nrf_52840/capture_nrf_52840.c b/capture_nrf_52840/capture_nrf_52840.c index 80978c7f6..253f0f592 100644 --- a/capture_nrf_52840/capture_nrf_52840.c +++ b/capture_nrf_52840/capture_nrf_52840.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "../config.h" @@ -314,25 +315,72 @@ int open_callback(kis_capture_handler_t *caph, uint32_t seqno, char *definition, } tcgetattr(localnrf->fd,&localnrf->oldtio); /* save current serial port settings */ - bzero(&localnrf->newtio, sizeof(localnrf->newtio)); /* clear struct for new port settings */ - /* set the baud rate and flags */ - localnrf->newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; + /* Build the new termios state from the device's current state and then + * normalise it. Avoid the bzero()+field-assignment pattern: with + * _FORTIFY_SOURCE=3 (enabled by -fhardened in GCC 14+) the checked + * memset can short-zero the struct if __builtin_object_size resolves + * to less than sizeof(struct termios), leaving stray bits (PARODD, + * IBAUD, etc.) in c_cflag that propagate straight through tcsetattr. + */ + localnrf->newtio = localnrf->oldtio; - /* ignore parity errors */ - localnrf->newtio.c_iflag = IGNPAR; + /* raw mode: clears canonical, echo, signals, IXON/IXOFF, OPOST, etc. */ + cfmakeraw(&localnrf->newtio); - /* raw output */ - localnrf->newtio.c_oflag = 0; + cfsetispeed(&localnrf->newtio, B115200); + cfsetospeed(&localnrf->newtio, B115200); - /* newtio.c_lflag = ICANON; */ + /* enable receiver, ignore modem control lines */ + localnrf->newtio.c_cflag |= (CLOCAL | CREAD | CS8); + localnrf->newtio.c_cflag &= ~(PARENB | PARODD | CSTOPB | CRTSCTS); + + /* ignore parity errors */ + localnrf->newtio.c_iflag |= IGNPAR; - localnrf->newtio.c_lflag &= ~ICANON; /* Set non-canonical mode */ - localnrf->newtio.c_cc[VTIME] = 1; /* Set timeout in deciseconds */ + /* short read timeout, no minimum */ + localnrf->newtio.c_cc[VMIN] = 0; + localnrf->newtio.c_cc[VTIME] = 1; + + /* one-shot diagnostic: log what we are about to push to the kernel. + * If c_cflag carries bits we never set (e.g. PARODD, B50<newtio.c_cflag, + (unsigned long) localnrf->newtio.c_iflag, + (unsigned long) localnrf->newtio.c_oflag, + (unsigned long) localnrf->newtio.c_lflag, + (unsigned) cfgetispeed(&localnrf->newtio), + (unsigned) cfgetospeed(&localnrf->newtio), + (unsigned) localnrf->newtio.c_cc[VMIN], + (unsigned) localnrf->newtio.c_cc[VTIME], + sizeof(localnrf->newtio)); /* flush and set up */ tcflush(localnrf->fd, TCIFLUSH); - tcsetattr(localnrf->fd, TCSANOW, &localnrf->newtio); + if (tcsetattr(localnrf->fd, TCSANOW, &localnrf->newtio) < 0) { + snprintf(msg, STATUS_MAX, "%s tcsetattr failed - %s", + localnrf->name, strerror(errno)); + pthread_mutex_unlock(&(localnrf->serial_mutex)); + return -1; + } + + /* explicitly assert RTS/DTR after tcsetattr. cdc-acm forwards these + * to the device's SET_CONTROL_LINE_STATE; some firmwares hold output + * until RTS is asserted. With CRTSCTS off, the kernel will not + * touch them automatically. + */ + { + int mctrl = 0; + if (ioctl(localnrf->fd, TIOCMGET, &mctrl) == 0) { + mctrl |= TIOCM_RTS | TIOCM_DTR; + ioctl(localnrf->fd, TIOCMSET, &mctrl); + } + } pthread_mutex_unlock(&(localnrf->serial_mutex));