The Linux Telephony Kernel API
At the operating system level, all devices are referred to by numbers. We look in /dev and see a collection of file names, but down deep Linux looks at devices based on a major and a minor number. Devices of a particular type all share a single major number; individual devices of that type must each have their own minor number. For example, if you do ls -al /dev/ttyS0 you'll see:
gherlein@tux:~/lj > ls -al /dev/ttyS0 crwxrwxr-x 1 root uucp 4, 64 Oct 27 06:23 /dev/ttyS0
Note that that the file permissions mask has the first character as a “c”, indicating that it's a character device. It's owned by root and is in the uucp group. The next two numbers are not file sizes, like you'd see on a normal file; they are the major and minor number. In this case ttyS0 has a major number of 4 and a minor number of 64.
The Linux Telephony API assigns the major device number of 100 to phone-type devices. Your distribution of Linux has probably not created the devices for you, like it has for /dev/ttyS*, /dev/audio and other older, commonly accepted device mappings. If /dev/phone* devices are not present on your system, you'll need to make them before using the Linux Telephony API. You can fix this yourself quickly with the following command (as root):
mknod /dev/phone0 c 100 0
This creates a new device file called /dev/phone0. It's a character device with a major number of 100 and a minor number of 0. Refer to the mknod man page for more details on this command. You only actually need enough device files for your hardware, but most folks create devices 0 15 by default.
Note that there is presently an error in the file /usr/src/linux/Documentation/devices.txt. This file, which provides official assignments for all major numbers it currently states that Linux telephony is to use major number 159 and that 100 is deprecated. This is an acknowledged error and will be fixed in future documentation. The correct major number for Linux telephony (and the number used in the kernel) is 100.
Alan Cox developed the phonedev module based on a similar approach that he took for the Video4Linux Project. There will be many vendors who create products capable of being a phone device. Rather than having multiple telephony product vendors, each requiring their own major number, they can all use major number 100 and the commonly defined /dev/phoneN (where N is the device number).
All of these must present a common basic interface to user-space software, that is, they must all follow the same common API.al, though they may provide extensions particular to their product. Vendors will create their own device driver module that will implement this common API as an external interface by dealing with the inner details of their particular hardware.
The phonedev module solved the problem of mapping the minor device number to a specific vendor-type module at runtime. The source code is in the files/usr/src/linux/drivers/telephony/phonedev.c and the header files, phonedev.h and telephony.h, are located in /usr/include/linux. Here's how it works.
Every phone device must use a phone_device structure (see Listing 1). Likewise, every phone device must call two functions to interact with the phonedev module in order to register and unregister itself. These are defined as:
extern int phone_register_device (struct phone_device *, int unit); extern void phone_unregister_device (struct phone_device *);
At load time, the phonedev module sets itself up and waits to service other telephony devices. When a telephony driver is loaded (by modprobe or insmod, as discussed later) it calls the phone_register_device function. A simple explanation of this function is that it keeps a pointer within the phonedev module to the phone_device structure, searches for the first open minor number and assigns that to the phone device, and then increments a counter to track the fact that something is using the phonedev module (to prevent it being unloaded while in use).
The practical implications of this are simple: the first telephony modules loaded will be assigned the first available (lowest numbered) minor numbers. This is crucial to understand in cases where modules from different vendors need to coexist on the same system, and specific assignment of a particular card is desired to match a particular minor number (a specific /dev/phoneN device). In other words, if you have a device from XYZ company and a device from ABC company, and you want ABC's card to be /dev/phone0, you'll have to make sure that the driver for ABC gets loaded first.
All devices must provide at least the basic functions to interact with the device: open, read, write, close, etc. These are all part of the “file operations structure” (see linux/fs.h for details). Each device defines the functions appropriate for itself.
Anytime a program opens a /dev/phoneN device, it's actually calling the fopen function defined in the phonedev module's file operations structure. This function performs the following operations:
It grabs the minor device number from /dev/phoneN inode.
It builds a string of the form char-major-%d-%d using the major and minor numbers. In the case of minor number 0 (corresponding to /dev/phone0), this string would be char-major-100-0.
It uses this string in a call to request_module to ask that the module be loaded. This has the same effect as if the program modprobe was called (in fact, it actually starts a separate kernel thread and executes modprobe in it). This ensures that, if the device is a module instead of a part of the kernel, kmod has a chance to load the module before phonedev tries to use it.
It then calls the fopen function in the phone device module to perform the actual act of opening the individual device.
As you can see, the phonedev module has two basic purposes: to assign minor numbers to phone devices dynamically at load time and provide a clean way to autoload needed phone device modules at run time. It's clear, though, that a good understanding of modprobe and module dependency is needed to fully understand the telephony modules and their interactions.
- High-Availability Storage with HA-LVM
- DNSMasq, the Pint-Sized Super Dæmon!
- March 2015 Issue of Linux Journal: System Administration
- Localhost DNS Cache
- Real-Time Rogue Wireless Access Point Detection with the Raspberry Pi
- Days Between Dates: the Counting
- The Usability of GNOME
- PostgreSQL, the NoSQL Database
- Linux for Astronomers
- You're the Boss with UBOS