29

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?

  • 1
    Try to use cat to read and echo to write. "Everything is a file" in unix :) – dchirikov Jun 20 '14 at 18:40
  • You can try opening a serial console on it. screen can do this, and miniterm – mikeserv Jun 20 '14 at 18:44
  • I was thinking in terms of a programmatic approach. If this is true and it's really that easy (being to just open and read the file), would you just write an infinite loop to continuously open, read, close the file and update when new data is present since the previous time? What happens if the 'tty' device attempts to write to the file if you have it open in your program? – sherrellbc Jun 20 '14 at 18:45
  • May be it helps: cmrr.umn.edu/~strupp/serial.html : open(), read(), write() and select(). Nothing special, it seems. – dchirikov Jun 20 '14 at 19:26
  • @sherrellbc - you can script screen and/or miniterm programatically. – mikeserv Jun 20 '14 at 21:55
37

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.

  • I am trying sudo echo Hello > /dev/tty4 when I am in the desktop environment but I get bash: /dev/tty4: Permission denied if I am not logged in to tty4. However if I am logged in to tty4, everything works fine. What is the reason for this? – Utku May 15 '16 at 10:22
  • @Utku, That is certainly some sort of permissions problem that are removed after authentication with the virtual terminal. Check the file permissions before and after you have logged in. ls -l /dev/tty4 – sherrellbc Jun 28 '16 at 18:42
  • 1
    the > /dev/tty4 part ist not part of the echo subprocess started by sudo but part of the sudo process itself, which is executed by the current user. file permissions for the current user apply instead of root. – Robin479 Dec 30 '16 at 19:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.