4.3. Accessing Serial Devices

Like all devices in a Unix system, serial ports are accessed through device special files, located in the /dev directory. There are two varieties of device files related to serial drivers, and there is one device file of each type for each port. The device will behave slightly differently, depending on which of its device files we open. We'll cover the differences because it will help you understand some of the configurations and advice that you might see relating to serial devices, but in practice you need to use only one of these. At some point in the future, one of them may even disappear completely.

The most important of the two classes of serial device has a major number of 4, and its device special files are named ttyS0, ttyS1, etc. The second variety has a major number of 5, and was designed for use when dialing out (calling out) through a port; its device special files are called cua0, cua1, etc. In the Unix world, counting generally starts at zero, while laypeople tend to start at one. This creates a small amount of confusion for people because COM1: is represented by /dev/ttyS0, COM2: by /dev/ttyS1, etc. Anyone familiar with IBM PC-style hardware knows that COM3: and greater were never really standardized anyway.

The cua, or “callout,” devices were created to solve the problem of avoiding conflicts on serial devices for modems that have to support both incoming and outgoing connections. Unfortunately, they've created their own problems and are now likely to be discontinued. Let's briefly look at the problem.

Linux, like Unix, allows a device, or any other file, to be opened by more than one process simultaneously. Unfortunately, this is rarely useful with tty devices, as the two processes will almost certainly interfere with each other. Luckily, a mechanism was devised to allow a process to check if a tty device had already been opened by another device before opening it. The mechanism uses what are called lock files. The idea was that when a process wanted to open a tty device, it would check for the existence of a file in a special location, named similarly to the device it intends to open. If the file does not exist, the process creates it and opens the tty device. If the file does exist, the process assumes another process already has the tty device open and takes appropriate action. One last clever trick to make the lock file management system work was writing the process ID (pid) of the process that had created the lock file into the lock file itself; we'll talk more about that in a moment.

The lock file mechanism works perfectly well in circumstances in which you have a defined location for the lock files and all programs know where to find them. Alas, this wasn't always the case for Linux. It wasn't until the Linux Filesystem Standard defined a standard location for lock files when tty lock files began to work correctly. At one time there were at least four, and possibly more locations chosen by software developers to store lock files: /usr/spool/locks/, /var/spool/locks/, /var/lock/, and /usr/lock/. Confusion caused chaos. Programs were opening lock files in different locations that were meant to control a single tty device; it was as if lock files weren't being used at all.

The cua devices were created to provide a solution to this problem. Rather than relying on the use of lock files to prevent clashes between programs wanting to use the serial devices, it was decided that the kernel could provide a simple means of arbitrating who should be given access. If the ttyS device were already opened, an attempt to open the cua would result in an error that a program could interpret to mean the device was already being used. If the cua device were already open and an attempt was made to open the ttyS, the request would block; that is, it would be put on hold and wait until the cua device was closed by the other process. This worked quite well if you had a single modem that you had configured for dial-in access and you occasionally wanted to dial out on the same device. But it did not work very well in environments where you had multiple programs wanting to call out on the same device. The only way to solve the contention problem was to use lock files! Back to square one.

Suffice it to say that the Linux Filesystem Standard came to the rescue and now mandates that lock files be stored in the /var/lock directory, and that by convention, the lock file name for the ttyS1 device, for instance, is LCK..ttyS1. The cua lock files should also go in this directory, but use of cua devices is now discouraged.

The cua devices will probably still be around for some time to provide a period of backward compatibility, but in time they will be retired. If you are wondering what to use, stick to the ttyS device and make sure that your system is Linux FSSTND compliant, or at the very least that all programs using the serial devices agree on where the lock files are located. Most software dealing with serial tty devices provides a compile-time option to specify the location of the lock files. More often than not, this will appear as a variable called something like LOCKDIR in the Makefile or in a configuration header file. If you're compiling the software yourself, it is best to change this to agree with the FSSTND-specified location. If you're using a precompiled binary and you're not sure where the program will write its lock files, you can use the following command to gain a hint:
strings binaryfile | grep lock
If the location found does not agree with the rest of your system, you can try creating a symbolic link from the lock directory that the foreign executable wants to use back to /var/lock/. This is ugly, but it will work.

4.3.1. The Serial Device Special Files

Minor numbers are identical for both types of serial devices. If you have your modem on one of the ports COM1: through COM4:, its minor number will be the COM port number plus 63. If you are using special serial hardware, such as a high-performance multiple port serial controller, you will probably need to create special device files for it; it probably won't use the standard device driver. The Serial-HOWTO should be able to assist you in finding the appropriate details.

Assume your modem is on COM2:. Its minor number will be 65, and its major number will be 4 for normal use. There should be a device called ttyS1 that has these numbers. List the serial ttys in the /dev/ directory. The fifth and sixth columns show the major and minor numbers, respectively:

$ ls -l /dev/ttyS*
0 crw-rw----   1 uucp     dialout    4,  64 Oct 13  1997 /dev/ttyS0
0 crw-rw----   1 uucp     dialout    4,  65 Jan 26 21:55 /dev/ttyS1
0 crw-rw----   1 uucp     dialout    4,  66 Oct 13  1997 /dev/ttyS2
0 crw-rw----   1 uucp     dialout    4,  67 Oct 13  1997 /dev/ttyS3

If there is no device with major number 4 and minor number 65, you will have to create one. Become the superuser and type:

# mknod -m 666 /dev/ttyS1 c 4 65
# chown uucp.dialout /dev/ttyS1
The various Linux distributions use slightly differing strategies for who should own the serial devices. Sometimes they will be owned by root, and other times they will be owned by another user, such as uucp in our example. Modern distributions have a group specifically for dial-out devices, and any users who are allowed to use them are added to this group.

Some people suggest making /dev/modem a symbolic link to your modem device so that casual users don't have to remember the somewhat unintuitive ttyS1. However, you cannot use modem in one program and the real device file name in another. Their lock files would have different names and the locking mechanism wouldn't work.