-1

My application should be able to communicate with different serial port settings which user chooses from UI. Every option works except software flow control. When I turn XON/XOFF on, it works with other peer. But when I turn off XON/XOFF, peer receives random amount of 0x00 bytes.

Is there anything I'm missing?

Note: I do exact same configuration to the peer as well. So when I turn on or off XON/XOFF option, I do the same for the peer.

int serialDevice = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);

Serial_Params_t serialSettings;
serialSettings.baudRate = 12; // means 115200
serialSettings.dataBits = 8;
serialSettings.hardwareFlowControl = 0;
serialSettings.parity = 0;
serialSettings.parityOdd = 0;
serialSettings.stopBits = 1;
serialSettings.xonxoff = 0;

setSerialSettings(serialDevice, &serialSettings);
//---------------------------------------------------------------
int8_t setSerialSettings(int serialDevice, Serial_Params_t *settings)
{
    struct termios2 tty;
    memset(&tty, 0, sizeof tty);

    // get current serial settings
    if (ioctl(serialDevice, TCGETS2, &tty) == -1)
    {
        sendLog("Can't get serial attributes | setSerialSettings", LOG_TYPE_ERROR);
        return FALSE;
    }

    // baudrate
    int32_t baudrates[] = {B110, B150, B300, B600, B1200, B2400, B4800, B9600, B9600, B19200, B38400, B57600, B115200};
    cfsetospeed(&tty, (speed_t)baudrates[settings->baudRate]);
    cfsetispeed(&tty, (speed_t)baudrates[settings->baudRate]);

    // enable input parity check
    tty.c_cflag |= INPCK;

    // data bits: CS5, CS6, CS7, CS8
    tty.c_cflag &= ~CSIZE;
    switch (settings->dataBits)
    {
    case 5:
        tty.c_cflag |= CS5;
        break;
    case 6:
        tty.c_cflag |= CS6;
        break;
    case 7:
        tty.c_cflag |= CS7;
        break;
    case 8:
    default:
        tty.c_cflag |= CS8;
        break;
    }

    // stop bit
    switch (settings->stopBits)
    {
    case 1:
    default:
        tty.c_cflag &= ~CSTOPB;
        break;
    case 2:
        tty.c_cflag |= CSTOPB;
    }

    // parity
    if (settings->parity == 1)
        tty.c_cflag |= PARENB;
    else
        tty.c_cflag &= ~PARENB;

    // odd/even parity
    if (settings->parityOdd == 1)
        tty.c_cflag |= PARODD;
    else
        tty.c_cflag &= ~PARODD;

    // flow control
    // XON/XOFF
    if (settings->xonxoff == 1)
        tty.c_cflag |= (IXON | IXOFF | IXANY);
    else
        tty.c_cflag &= ~(IXON | IXOFF | IXANY);

    // enable RTS/CTS
    if (settings->hardwareFlowControl == 1)
        tty.c_cflag |= CRTSCTS;
    else
        tty.c_cflag &= ~CRTSCTS;

    tty.c_cc[VMIN] = 1;            // raise read flag even you read 1 byte
    tty.c_cc[VTIME] = 50;          // 1 seconds read timeout (deciseconds)
    tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines

    // non-canonical mode
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    // flush port & apply attributes
    tcflush(serialDevice, TCIFLUSH);
    if (tcsetattr(serialDevice, TCSANOW, &tty) != 0)
    {
        sendLog("Can't set serial attributes | setSerialSettings", LOG_TYPE_ERROR);
        return FALSE;
    }
    return TRUE;
}
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
Mustafa Chelik
  • 2,113
  • 2
  • 17
  • 29
  • 1
    When two devices or programs are trying to communicate, the characteristics of *both* are important. Are you sure the remote peer is not relying on software flow control to be enabled? Whether explicitly, or perhaps just by being bugged in the case that software flow control is disabled? – John Bollinger Mar 14 '19 at 13:16
  • @JohnBollinger yes, I quadro-checked every possibility before posting this question. I even used different applications for the peer. Results are the same unfortunately. – Mustafa Chelik Mar 14 '19 at 13:21
  • 3
    Please post the include files used and definition of `Serial_Params_t`. A [MCVE] would even be better. – chux - Reinstate Monica Mar 14 '19 at 13:49
  • 2
    It's suspicious that you are using an ioctl to retrieve the serial port properties, yet using `tcsetattr()` to update them. Particularly so because `struct termios2` is a Linuxism, and the (Linux-)standard header in which it is defined is incompatible with the GLIBC `termios.h`, so you would need to be doing something weird to even make your code compile. Moreover, GLIBC's `struct termios` is incompatible with the kernel's `struct termios2` (and with the kernel's `struct termios`, too). – John Bollinger Mar 14 '19 at 13:56
  • In your `Serial_Params_t`, you have set hardware flow control off (CRTSCTS) and software flow control off (XON/XOFF). For the serial devices I work with it is usually hardware flow control. But backing up, what does your hardware manual tell you to do? You are also missing a couple of flags that may be important (status lines and post processing). Here's what my terminal support file looks like for modems: [sample linux terminal config](https://pastebin.com/pu6Frg7D). You should also consider using `cfmakeraw` for the baseline terminal configuration. – jww Mar 14 '19 at 14:06
  • 3
    The stream of 0's may be due to `tcsendbreak`. I think we need a [MCVE](http://stackoverflow.com/help/mcve), and details of the hardware. Also see [Why is tcsendbreak() necessary for serial communication to start working?](https://stackoverflow.com/q/39964547/608639) and [the comment](https://stackoverflow.com/questions/39964547/why-is-tcsendbreak-necessary-for-serial-communication-to-start-working#comment67209706_39964547), *"Failing to follow Setting Terminal Modes Properly is one way to break portability."* – jww Mar 14 '19 at 14:36

2 Answers2

3

I cannot explain specifically why your peer is receiving extraneous 0x00 bytes because you do not present any code responsible for sending anything at all, but the code you present for setting the serial port properties is faulty several times over. Issues include:

  • Use of mismatched system interfaces. You may use appropriate ioctls to retrieve and update terminal attributes, or you may use tcgetattr and tcsetattr, but you must not mix & match. The data structures in which the Linux ioctls deal are not interchangeable with the one that GLIBC's termios functions work with, though they are similar enough, especially in the type, names, and order of the flag members, that that might not reliably cause noticeable errors. You must have played some tricks and / or ignored some important compiler warnings to even get your code to compile.

    Note that mixing & matching applies not only to retrieving and setting the overall terminal settings, but also to all use of the termios.h functions for modifying a terminal settings structure, such as cfgetispeed and cfgetospeed. You may use these with a settings structure obtained via tcgetattr or, less usefully, with a directly-declared instance of the struct termios defined by termios.h, but not with one obtained via ioctl. On the flip side, a structure suitable for use with any of the termios.h functions is not suitable for use with any of the ioctls for manipulating terminal properties.

  • Setting flag bits in the wrong flag variables. Of particular relevance to the question, you are setting the IXON, IXOFF, and IXANY bits of c_cflag, but these apply to the c_iflag member, not c_cflag. The leading I in their names is mnemonic for that, though not all of the flag macros follow that convention. You are also setting INPCK in the wrong member.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thank you very much. I changed both get and set to `ioctl` and changed the flags as you mentioned. I can communicate with XON/XOFF disabled. Now sir, turns out I can't communicate with certain baudrates. I can communicate via 9600 and 115200 but can't via 14400. Do you know why this happens? I updated the code. Thank you :) – Mustafa Chelik Mar 14 '19 at 18:34
  • @MustafaChelik, if you have a followup question about revised code then please pose it as a separate question. Especially, do not modify the code presented in the question when you have answers referring to it (edits rolled back). – John Bollinger Mar 14 '19 at 18:44
  • However, my general advice would be to use (only) the `termios.h` interfaces, getting rid of all traces of the ioctls and headers related specifically to them. Nothing in what you've presented demonstrates a need for `ioctl`, and the `termios.h` interfaces are cleaner, more convenient, and better documented. – John Bollinger Mar 14 '19 at 18:53
  • I can't use `termios.h` because I need `struct termios2`. I posted new question as you requested. I'd be happy if you can check it. My new question is here: https://stackoverflow.com/questions/55170488/cant-communicate-with-certain-buadrates – Mustafa Chelik Mar 14 '19 at 19:23
  • @MustafaChelik, as I said, I see no reason in what you presented here why you need the `ioctl` interface. Including its `struct termios2`. In particular, on Linux, you do not need it to support the transmission speeds you are trying to accommodate. – John Bollinger Mar 14 '19 at 19:28
  • If you want to use custom baudrate (which my application should have this option) you have to use `termios2` because it has `c_ispeed` member. Do you know any other way? I'd be happy to hear it sir – Mustafa Chelik Mar 14 '19 at 19:29
  • Also the 14400 baudrate which is common is not pre-defined in baudrate list in termios. So to use the 14400 baudrate you have go for custom baudrate settings which I mentioned before. – Mustafa Chelik Mar 14 '19 at 19:31
  • 2
    Very well, @MustafaChelik. But the 14400 line speed is not among those supported by the code you presented, so again, nothing *in what you presented* exhibits a need for the `ioctl` interface. This is all the more reason for the followup to be a separate question (and thank you for making it one). – John Bollinger Mar 14 '19 at 19:35
  • The reason is there is no `B14400` defined in the termios. So if you look closely there is two `B9600` in the list just to prevent shifting my list (I have the same list in web). Thank you for your time. – Mustafa Chelik Mar 14 '19 at 19:38
1

But when I turn off XON/XOFF, peer receives random amount of 0x00 bytes.

This may mean the sender is sending data faster than the receiver can cope with it.

Either

  • Reduce the rate of sending data (This is not so much a baud thing, more like do not send so much data at once.)

  • Use hardware flow control (and the wire/cables that support it).

  • Improve the handling the receivers incoming data.


Note: Other possibilities exit to explain the zeroes.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    I could imagine data loss (dropouts) if the sender were sending too fast for the receiver to handle, but why sequences of `0x00` bytes? – John Bollinger Mar 14 '19 at 14:00
  • @John - I believe `tcsendbreak` sends the stream of 0's and it may have something to do with `ICANON`. What I don't know is why `tcsendbreak` is being used. I don't think we have a good working example from OP, and we know nearly nothing about his hardware. – jww Mar 14 '19 at 14:33
  • @JohnBollinger A buffer overflow may signal as count of zero bytes - yet [@jww](https://stackoverflow.com/questions/55163187/cant-communicate-when-software-flow-control-is-disabled-for-serial-port/55164367?noredirect=1#comment97069498_55164367) idea is a more likely scenario. – chux - Reinstate Monica Mar 14 '19 at 14:40
  • 1
    I suppose that's conceivable, @jww, but receivers oughtn't to interpret breaks as sequences of *properly framed* `0x00` bytes. Maybe all the clients the OP has tried are ignoring framing errors, though. – John Bollinger Mar 14 '19 at 14:53