Terminal Line Speed Linux Terminal Programming

Terminal Line Speed
Chapter 62.7 — Linux Terminal Programming | EmbeddedPathashala
Topic
Line Speed Control
Functions
cfgetospeed / cfsetospeed
Header
<termios.h>

What is Terminal Line Speed?

In serial communication, line speed (also called baud rate) defines how fast data is transferred between the computer and a terminal or serial device — measured in bits per second (bps).

Think of a water pipe: the line speed is how fast water flows through it. If the terminal expects 9600 bps and your program sends at 115200 bps, communication breaks down — just like too much water in a narrow pipe.

Linux uses the termios structure to store and manage all terminal settings, including line speed. You read and write the speed using the functions cfgetospeed(), cfgetispeed(), cfsetospeed(), and cfsetispeed().

Key Terms

speed_t cfgetospeed cfgetispeed cfsetospeed cfsetispeed tcgetattr tcsetattr TCSAFLUSH B9600 B38400 CBAUD c_cflag

🔔 The speed_t Type and Baud Rate Constants

Line speed is stored using the speed_t data type. You never assign a raw number like 9600 directly — instead, you use symbolic constants defined in <termios.h>.

Why symbolic constants? Because terminals are built to support only specific standardized speeds. These speeds come from dividing a base clock rate (like 115,200 Hz on PCs) by whole numbers. For example: 115200 ÷ 12 = 9600, 115200 ÷ 3 = 38400, and so on.

Constant Speed (bps) Common Use Case
B300 300 Very old modems
B2400 2400 Slow serial devices
B9600 9600 Classic default for GPS, modems
B19200 19200 Faster serial devices
B38400 38400 Moderately fast serial
B115200 115200 Modern embedded boards (UART)

Important: On Linux, line speed is stored inside the c_cflag field of the termios structure using the CBAUD mask and CBAUDEX flag. The standard deliberately does not say exactly where speed is stored — to keep it portable.

🔄 How Reading and Setting Speed Works

The general pattern for changing any terminal setting (including speed) in Linux is always this 3-step flow:

Step 1
tcgetattr()
Read current settings
into termios struct
Step 2
cfsetospeed()
Modify the speed
field in struct
Step 3
tcsetattr()
Write modified struct
back to driver

You never call cfsetospeed() alone and expect the kernel to know — you always apply it back with tcsetattr().

📄 Function Signatures
Function Purpose Returns
cfgetospeed(const struct termios *tp) Get current output speed speed_t value
cfgetispeed(const struct termios *tp) Get current input speed speed_t value
cfsetospeed(struct termios *tp, speed_t speed) Set output speed in struct 0 on success, -1 on error
cfsetispeed(struct termios *tp, speed_t speed) Set input speed in struct 0 on success, -1 on error

💻 Code Example: Read and Change Line Speed

This example opens a serial port, reads the current output speed, prints it, then changes it to 38400 bps.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

/* Helper to convert speed_t constant to human-readable bps string */
const char *speed_to_str(speed_t s)
{
    switch (s) {
        case B300:    return "300";
        case B2400:   return "2400";
        case B9600:   return "9600";
        case B19200:  return "19200";
        case B38400:  return "38400";
        case B115200: return "115200";
        default:      return "unknown";
    }
}

int main(void)
{
    int fd;
    struct termios tp;
    speed_t rate;

    /* Open the serial port in read-write, no controlling terminal */
    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
    if (fd == -1) {
        perror("open /dev/ttyS0");
        exit(EXIT_FAILURE);
    }

    /* Step 1: Read current terminal settings into tp */
    if (tcgetattr(fd, &tp) == -1) {
        perror("tcgetattr");
        exit(EXIT_FAILURE);
    }

    /* Step 2a: Get and print current output speed */
    rate = cfgetospeed(&tp);
    printf("Current output speed: %s bps\n", speed_to_str(rate));

    /* Step 2b: Set new output speed in tp (not applied yet) */
    if (cfsetospeed(&tp, B38400) == -1) {
        perror("cfsetospeed");
        exit(EXIT_FAILURE);
    }

    /* Also set input speed to match (good practice) */
    if (cfsetispeed(&tp, B38400) == -1) {
        perror("cfsetispeed");
        exit(EXIT_FAILURE);
    }

    /* Step 3: Apply the modified termios back to the driver.
     * TCSAFLUSH: wait for output to drain, discard pending input */
    if (tcsetattr(fd, TCSAFLUSH, &tp) == -1) {
        perror("tcsetattr");
        exit(EXIT_FAILURE);
    }

    /* Verify: read back the speed after applying */
    if (tcgetattr(fd, &tp) == -1) {
        perror("tcgetattr (verify)");
        exit(EXIT_FAILURE);
    }
    rate = cfgetospeed(&tp);
    printf("New output speed: %s bps\n", speed_to_str(rate));

    close(fd);
    return 0;
}

How to compile and test:

gcc -o line_speed line_speed.c
# Run as root or with appropriate permissions on a real serial port
sudo ./line_speed

Note on tcsetattr flags:

Flag Meaning
TCSANOW Apply change immediately
TCSADRAIN Apply after all output has been sent
TCSAFLUSH Wait for output to drain AND discard pending input

💡 Special Case: Setting Input Speed to 0

If you call cfsetispeed(&tp, 0) (speed = 0), it means: “when tcsetattr() is called, set the input speed to whatever the output speed is at that moment.”

This is a portable trick for keeping input and output speeds in sync on systems that maintain them as two separate values. On Linux, both speeds actually share the same internal field, so in practice they are always equal anyway.

/* Portable way to keep input speed = output speed */
cfsetospeed(&tp, B115200);
cfsetispeed(&tp, 0);          /* 0 means: match the output speed */
tcsetattr(fd, TCSAFLUSH, &tp);

🎓 Interview Questions — Terminal Line Speed

Q1. What does the speed_t data type represent in Linux terminal programming?

A: It is the data type used to hold a terminal line speed (baud rate). It stores symbolic constants like B9600 or B115200, not raw numeric values directly. It is defined in <termios.h>.

Q2. Why does Linux use symbolic constants like B9600 instead of the number 9600?

A: Terminals only support a limited set of standardized speeds derived by dividing a base clock rate by whole numbers. The actual encoding of the speed inside the termios structure is implementation-specific. Using symbolic constants keeps the code portable across different UNIX systems, regardless of how the speed is internally encoded.

Q3. What is the correct sequence to change the baud rate of a serial terminal in Linux?

A: (1) Call tcgetattr(fd, &tp) to read the current settings. (2) Call cfsetospeed(&tp, B38400) (and optionally cfsetispeed()) to modify the speed in the struct. (3) Call tcsetattr(fd, TCSAFLUSH, &tp) to apply the change to the driver.

Q4. Where does Linux store the terminal line speed inside the termios structure?

A: Linux stores the line speed in the c_cflag field using the CBAUD mask and CBAUDEX flag. SUSv3 deliberately leaves the storage location unspecified for portability. The c_ispeed and c_ospeed fields that appear in the Linux termios structure are actually unused.

Q5. What happens when you pass 0 as the speed to cfsetispeed()?

A: It means “set the input speed equal to the output speed when tcsetattr() is applied.” It is a portable way to synchronize input and output speeds on systems where they can differ. On Linux, both speeds share the same field anyway.

Q6. What is the difference between TCSANOW, TCSADRAIN, and TCSAFLUSH in tcsetattr()?

A: TCSANOW applies the change immediately. TCSADRAIN waits until all pending output has been transmitted before applying. TCSAFLUSH waits for output to drain and also discards any pending unread input. For baud rate changes, TCSAFLUSH is the safest choice.

Q7. Can input and output line speeds be different in Linux?

A: Theoretically, cfsetispeed() and cfsetospeed() allow setting them separately. But on Linux, both speeds share the same internal field, so they are always equal. On many real hardware terminals also, both speeds must match for correct communication.

Continue Learning

Next: Terminal Line Control — tcdrain, tcflush, tcflow, tcsendbreak

EmbeddedPathashala Home

Leave a Reply

Your email address will not be published. Required fields are marked *