How to set a non-standard baudrate on a serial port device on Linux?

前端 未结 1 1589
北恋
北恋 2021-01-26 01:58

What are the ways of setting custom baudrates on Linux?

An answer to this question must be at a level of userland low-level APIs (ioctl, etc.) above the lev

相关标签:
1条回答
  • 2021-01-26 02:38

    Things are, unfortunately, driver-dependent. Good drivers will implement all of the methods below. Bad drivers will implement only some of the methods. Thus you need to try them all. All of the methods below are implemented in the helper functions in linux/drivers/tty/serial/serial_core.c.

    The following 4 choices are available.

    1. Standard baud rates are set in tty->termios->c_cflag. You can choose from:

          B0
          B50
          B75
          B110
          B134
          B150
          B200
          B300
          B600
          B1200
          B1800
          B2400
          B4800
          B9600
          B19200
          B38400
          B57600
          B115200
          B230400
      
    2. If you need rates not listed above, e.g. 460800 (this is a deprecated hack that the kernel developers wish to die, per the source code comments):

      • set tty->termios->c_cflag speed to B38400

      • call TIOCSSERIAL ioctl with (struct serial_struct) set as follows:

        serial->flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP]
        // this is an assertion, i.e. what your code must achieve, not how
        

        This sets alternate speed to HI: 57600, VHI: 115200, SHI: 230400, WARP: 460800

    3. You can set an arbitrary speed using alt_speed as follows:

      • Set tty->termios->c_cflag speed to B38400. This is unrelated to the speed you chose!

      • Set the intended speed in tty->alt_speed. It gets ignored when alt_speed==0.

    4. You can also an arbitrary speed rate by setting custom divisor as follows:

      • Set tty->termios->c_cflag speed to B38400. This is unrelated to the speed you chose!

        bool set_baudrate(int fd, long baudrate) {
          struct termios term;
          if (tcgetattr(fd, &term)) return false;
          term.c_cflag &= ~(CBAUD | CBAUDEX);
          term.c_cflag |= B38400;
          if (tcsetattr(fd, TCSANOW, &term)) return false;
          // cont'd below
        
      • Call TIOCSSERIAL ioctl with struct serial_struct set as follows:

        serial->flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST
        serial->custom_divisor == serial->baud_base / your_new_baudrate
        // these are assertions, i.e. what your code must achieve, not how
        

      How to do it? First get the structure filled (including baud_base you need) by calling TIOCGSERIAL ioctl. Then modify it to indicate the new baudrate and set it with TIOCSSERIAL:

            // cont'd
            struct serial_struct serial;
            if (ioctl(fd, TIOCGSERIAL, &serial)) return false;
            serial->flags &= ~ASYNC_SPD_MASK;
            serial->flags |= ASYNC_SPD_CUST;
            serial->custom_divisor = serial->baud_base / baudrate.
            if (ioctl(fd, TIOCSSERIAL, &serial)) return false;
            return true;
         }
      
    0 讨论(0)
提交回复
热议问题