USB TTY – How to Read/Write to tty* Device

ttyusb

I have a device that sends information over USB to my computer. Arch Linux sets up this device by creating a file named ttyUSB0 in /dev/. I have been using GTKterm to receive this incoming information and display it in an emulated terminal window.

My question is, how exactly does GTKterm read/write to this ttyUSB0 file, and where might I start learning how to implement similar functionality? That is, in the most basic form, how might I write a character to ttyUSB0, or, in contrast, receive a byte and write it to a file?

Best Answer

TTYs are files that you can use just like any other. You can open them with the standard file-opening tools of your language and read or write from them. They have some special behaviour that's different to "ordinary" files, but the basics are the same. I'll cover some of the special cases at the end, but first, an experiment.

One interesting thing you can do straight from a regular terminal. Run tty and it will print a line like:

/dev/pts/2

That's the TTY device your terminal is running in. You can write something to that terminal:

$ echo Hello > /dev/pts/2
Hello
$

You can even read from it:

$ read X < /dev/pts/2
hello
$ echo $X
hello
$

(read X is sh's "read a line from standard input into variable X" command; the < is to use /dev/pts/2 as standard input for the read command; the first "hello" I typed, and the second was printed out).

If you open up another shell, say by using screen or xterm, you can run run echo spooky > /dev/pts/2 in that shell to make the text appear on your original terminal, and the same for the other commands. All of this is just your shell opening a file without knowing it's a TTY.


Here is a very simple C program that does just what you asked, and writes a single character to /dev/pts/3, then reads a single byte back from it:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>    
int main() {
    char byte;
    int fd = open("/dev/pts/3", O_RDWR);
    write(fd, "X", 1);
    ssize_t size = read(fd, &byte, 1);
    printf("Read byte %c\n", byte);
    return 0;
}

A real TTY device that's attached to a shell or terminal emulator will have interesting behaviour there, but you should get something back.


To access a terminal you need to have permission to use it. Those are just the standard file permissions you see with ls -l and set with chmod: you need read permission to open the file and read it, and write permission to write into it. The TTYs that back your terminal will be owned by you, but another user's TTY won't, and TTYs for USB devices may or may not be, depending on your configuration. You can change the permissions in the same way as always.

As far as writing a program to work with it goes, you don't need to do much special. You can see in the example that one thing you don't need to do is close the file every time to have your data read by the other end: the TTY files act like pipelines, just pushing data in both directions as it comes in. When I wrote text to the TTY it appeared immediately, and when I read from it afterwards there wasn't anything waiting for me already. It's not like writing to a regular file where the data gets saved on disk - it gets passed on immediately to the other side, or stored in memory until someone reads it.

You may want to use the select function so that you can do other things while you wait for the device to say something, but if you're happy to just wait for data to come through you can just use blocking reads and let the OS do the lifting.

One thing to keep in mind is that there is can be limited buffer size in the kernel, and if you write a lot of data at once you may end up blocking without meaning to. If that's likely to be a problem, use non-blocking IO with open("/dev/...", O_RDWR | O_NONBLOCK). The principle will be the same either way.

Related Question