The Linux Telephony Kernel API

Herlein explains the integration of the telephony device driver into the Linux kernel.

Controlling Phone Devices—the ioctl System
Call

Commands for the phone device to perform certain actions are
not written to the phone device using a write call—only audio data
is written to the device. To control the phone device, a set of
ioctl functions are defined to handle the basic phone activities.
This basic set of ioctl functions are defined in
/usr/include/linux/telephony.h. Vendors who wish
to extend that basic set of capabilities may do so, but those
functions are limited to their own device driver and are outside
the scope of the common Linux telephony API.

An example will best illustrate this potential. Using the
telephony API with a Quicknet Internet PhoneJACK card and a phone
plugged into the card (called an FXS port or POTS port by telephony
folks), Listing 2 shows a brief program to ring the phone.

You'll notice that the device is opened, defaulting to
/dev/phone0 if the user does not provide a specific device name on
the command line. The maximum number of rings is set with an ioctl
call using the PHONE_MAXRINGS constant. The phone is then
instructed to ring using the PHONE_RING ioctl. This example program
is a simplified version of the LGPL module
ring.c found in Quicknet's
Software Developer Kit (SDK). It's too simple to do anything more
than illustrate the technique and demonstrate the use of ioctls to
control a phone device, but real-world programs need not be much
more complicated, the API is fairly simple. All the Linux telephony
API ioctl constants are defined in the header file
/etc/include/linux/telephony.h.

It's this basic fact that allows software written for sound
cards to be adapted easily for use with phone devices; like sound
cards, the only data written using the read and write system calls
is audio data. In addition, the low-level constants used in the
defined telephony ioctl calls were designed to avoid conflict with
existing sound card ioctls. Porting an application that expects a
sound card to use a phone card instead will primarily involve
handling the errors returned from the sound card ioctl calls your
code makes. It should be possible (though perhaps not easy) to
write a wrapper that opens the telephony device and spawns a child
process (inheriting the open file descriptor to the phone device)
that runs software expecting a sound card interface. While it's not
totally transparent, it's possible and should not actually be that
difficult.

Asynchronous Event Notification

Events that occur on the telephony side of the device need to
be communicated to the user-space software running the phone. Old
and crude methods for this required the software to poll the device
continuously for status and changes. The Linux telephony API avoids
that, of course, and provides two different techniques, both
generically called “asynchronous event notification”. The first
method uses signals for indication, and the second uses the
“exception bit” in the file descriptor set for exceptions. I'll
cover both techniques in order.

The use of signals for event notification requires three
steps: first, prepare and declare a signal handler function for the
SIGIO signal; second, set the process ID (PID) for the running
process to receive the signal; third, enable the generation of the
signal on the open files descriptor using the fcntl system call. An
excellent description of these steps can be found in Chapter 12 of
Advanced Programming in the UNIX Environment
by W. Richard Stevens (a worthwhile book to have in any case).
Again, a short example will probably clear this up. Assuming you
have an open file descriptor ixj1 that is an open phone device, you
can enable asynchronous event notification with signals with the
following code snip:

and a related signal handling function
(getdata in the code above) to process the data.
When you get the signal, you still do not know what kind of event
occurred—only that an event did occur. Your program would then
have to make an ioctl call to the phone device to ask what kind of
event was detected (more on this below). In addition, if your
program has more than one open phone device file descriptor, you
will not know which one generated the signal. And, signals can be
complicated to deal with and can be unreliable in a multi-threaded
program and, so, are avoided by some developers. These factors
limit the effectiveness of this method, leading to the more useful
case of using the “exception bit” in the file descriptor set for
exceptions.

It's common for programs to set read and write exception sets
and then use the select( ) system
call to wait for a file descriptor to be readable or writable. A
less well-known aspect of the select( ) call is the “exception
set”. The Linux telephony API uses this exception set to signal a
process that a telephony event has occurred. Listing 3 presents a
simple example using the file descriptor ixj1.

This extremely simple (and not really useful) example is
purely to illustrate the technique of using the exception set and
select( ) to detect events. The phone device driver will set the
appropriate bit in the exception set if an event has occurred,
causing select( ) to return. The user can screen the read, write
and exception descriptor sets to determine if its own file
descriptor was marked by the device driver as ready for that kind
of operation. If data is ready to be read, the statement
FD_ISSET(ixj1,&rfds) will return
TRUE;
FD_ISSET(ixj1,&wfds) statement returns
TRUE if the device is ready to be written to.
And, if a telephony event has occurred, the
FD_ISSET(ixj1,&efds) will return
TRUE. So, how do you detect what specific event
occurred?

The API provides a special PHONE_EXCEPTION ioctl call and an
associated telephony_exception structure to decode the return
value. This call will set bits in the structure that indicate which
of the telephony events occurred (there may have been several). In
the above example “hookstate” bit is examined in the statement
if(ixje.bits.hookstate), and if that bit is set
it indicates a change of status. An ioctl call is then made to
determine if the phone is on or off hook. Real-world code would
have used a large select or an extended if-else-if ladder to
examine the contents of ixje.bits after the PHONE_EXCEPTION ioctl
call. A detailed explanation of how to use this technique is beyond
the scope of this article, but please refer to the
/usr/include/linux/telephony.h file for details
of what kinds of events can be detected.