18.2. tty_driver Function Pointers
Finally, the tiny_tty driver declares four
function pointers.
18.2.1. open and close
The open function is called by the tty core when
a user calls open on the device node the tty
driver is assigned to. The tty core calls this with a pointer to the
tty_struct structure assigned to this device, and
a file pointer. The open field must be set by a
tty driver for it to work properly; otherwise,
-ENODEV is returned to the user when open is
called.
When this open function is called, the tty
driver is expected to either save some data within the
tty_struct variable that is passed to it, or save
the data within a static array that can be referenced based on the
minor number of the port. This is necessary so the tty driver knows
which device is being referenced when the later close, write, and
other functions are called.
The tiny_tty driver saves a pointer within the
tty structure, as can be seen with the following code:
static int tiny_open(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny;
struct timer_list *timer;
int index;
/* initialize the pointer in case something fails */
tty->driver_data = NULL;
/* get the serial object associated with this tty pointer */
index = tty->index;
tiny = tiny_table[index];
if (tiny = = NULL) {
/* first time accessing this device, let's create it */
tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
if (!tiny)
return -ENOMEM;
init_MUTEX(&tiny->sem);
tiny->open_count = 0;
tiny->timer = NULL;
tiny_table[index] = tiny;
}
down(&tiny->sem);
/* save our structure within the tty structure */
tty->driver_data = tiny;
tiny->tty = tty;
In this code, the tiny_serial structure is saved
within the tty structure. This allows the
tiny_write,
tiny_write_room, and
tiny_close functions to retrieve the
tiny_serial structure and manipulate it properly.
The tiny_serial structure is defined as:
struct tiny_serial {
struct tty_struct *tty; /* pointer to the tty for this device */
int open_count; /* number of times this port has been opened */
struct semaphore sem; /* locks this structure */
struct timer_list *timer;
};
As we've seen, the open_count
variable is initialized to 0 in the open call the
first time the port is opened. This is a typical reference counter,
needed because the open and
close functions of a tty driver can be called
multiple times for the same device in order to allow multiple
processes to read and write data. To handle everything correctly, a
count of how many times the port has been opened or closed must be
kept; the driver increments and decrements the count as the port is
used. When the port is opened for the first time, any needed hardware
initialization and memory allocation can be done. When the port is
closed for the last time, any needed hardware shutdown and memory
cleanup can be done.
The rest of the tiny_open function shows how to
keep track of the number of times the device has been opened:
++tiny->open_count;
if (tiny->open_count = = 1) {
/* this is the first time this port is opened */
/* do any hardware initialization needed here */
The open function must return either a negative
error number if something has happened to prevent the open from being
successful, or a 0 to indicate success.
The close function pointer is called by the tty
core when close is called by a user on the file
handle that was previously created with a call to
open. This indicates that the device should be
closed at this time. However, since the open
function can be called more than once, the close
function also can be called more than once. So this function should
keep track of how many times it has been called to determine if the
hardware should really be shut down at this time. The
tiny_tty driver does this with the following
code:
static void do_close(struct tiny_serial *tiny)
{
down(&tiny->sem);
if (!tiny->open_count) {
/* port was never opened */
goto exit;
}
--tiny->open_count;
if (tiny->open_count <= 0) {
/* The port is being closed by the last user. */
/* Do any hardware specific stuff here */
/* shut down our timer */
del_timer(tiny->timer);
}
exit:
up(&tiny->sem);
}
static void tiny_close(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->driver_data;
if (tiny)
do_close(tiny);
}
The tiny_close
function just calls the
do_close
function to do the real work of closing the device. This is done so
that the shutdown logic does not have to be duplicated here and when
the driver is unloaded and a port is open. The
close function has no return value, as it is not
supposed to be able to fail.
18.2.2. Flow of Data
The
write
function call is called by the user when there is data to be sent to
the hardware. First the tty core receives the call, and then it
passes the data on to the tty driver's
write function. The tty core also tells the tty
driver the size of the data being sent.
Sometimes, because of the speed and buffer capacity of the tty
hardware, not all characters requested by the writing program can be
sent at the moment the write function is called.
The write function should return the number of
characters that was able to be sent to the hardware (or queued to be
sent at a later time), so that the user program can check if all of
the data really was written. It is much easier for this check to be
done in user space than it is for a kernel driver to sit and sleep
until all of the requested data is able to be sent out. If any errors
happen during the write call, a negative error
value should be returned instead of the number of characters that
were written.
The write function can be called from both
interrupt context and user context. This is important to know, as the
tty driver should not call any functions that might sleep when it is
in interrupt context. These include any function that might possibly
call schedule, such as the common functions
copy_from_user, kmalloc,
and printk. If you really want to sleep, make
sure to check first whether the driver is
in interrupt context by
calling in_interrupt.
This sample tiny tty driver does not connect to any real hardware, so
its write function simply records in the kernel debug log what data
was supposed to be written. It does this with the following code:
static int tiny_write(struct tty_struct *tty,
const unsigned char *buffer, int count)
{
struct tiny_serial *tiny = tty->driver_data;
int i;
int retval = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count)
/* port was not opened */
goto exit;
/* fake sending the data out a hardware port by
* writing it to the kernel debug log.
*/
printk(KERN_DEBUG "%s - ", _ _FUNCTION_ _);
for (i = 0; i < count; ++i)
printk("%02x ", buffer[i]);
printk("\n");
exit:
up(&tiny->sem);
return retval;
}
The write function can be called when the tty
subsystem itself needs to send some data out the tty device. This can
happen if the tty driver does not implement the
put_char function in the
tty_struct. In that case, the tty core uses the
write function callback with a data size of 1.
This commonly happens when the tty core wants to convert a newline
character to a line feed plus a newline character. The biggest
problem that can occur here is that the tty driver's
write function must not return
0 for this kind of call. This means that the
driver must write that byte of data to the device, as the caller (the
tty core) does not buffer the data and try again at a later time. As
the write function can not determine if it is
being called in the place of put_char, even if
only one byte of data is being sent, try to implement the
write function so it always writes at least one
byte before returning. A number of the current USB-to-serial tty
drivers do not follow this rule, and because of this, some terminals
types do not work properly when connected to them.
The write_room function is called when the tty
core wants to know how much room in the write buffer the tty driver
has available. This number changes over time as characters empty out
of the write buffers and as the write function
is called, adding characters to the buffer.
static int tiny_write_room(struct tty_struct *tty)
{
struct tiny_serial *tiny = tty->driver_data;
int room = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count) {
/* port was not opened */
goto exit;
}
/* calculate how much room is left in the device */
room = 255;
exit:
up(&tiny->sem);
return room;
}
18.2.3. Other Buffering Functions
The
chars_in_buffer
function in the tty_driver structure is not
required in order to have a working tty driver, but it is
recommended. This function is called when the tty core wants to know
how many characters are still remaining in the tty
driver's write buffer to be sent out. If the driver
can store characters before it sends them out to the hardware, it
should implement this function in order for the tty core to be able
to determine if all of the data in the driver has drained out.
Three functions callbacks in the tty_driver
structure can be used to flush any remaining data that the driver is
holding on to. These are not required to be implemented, but are
recommended if the tty driver can buffer data before it sends it to
the hardware. The first two function callbacks are called
flush_chars and
wait_until_sent. These functions are called when
the tty core has sent a number of characters to the tty driver using
the put_char function callback. The
flush_chars function callback is called when the
tty core wants the tty driver to start sending these characters out
to the hardware, if it hasn't already started. This
function is allowed to return before all of the data is sent out to
the hardware. The wait_until_sent function
callback works much the same way; but it must wait until all of the
characters are sent before returning to the tty core or until the
passed in timeout value has expired, whichever
occurrence happens first. The tty driver is allowed to sleep within
this function in order to complete it. If the timeout value passed to
the wait_until_sent function callback is set to
0, the function should wait until it is finished
with the operation.
The remaining data flushing function callback is
flush_buffer. It is called by the tty core when
the tty driver is to flush all of the data still in its write buffers
out of memory. Any data remaining in the buffer is lost and not sent
to the device.
18.2.4. No read Function?
With only these functions, the tiny_tty driver
can be registered, a device node opened, data written to the device,
the device node closed, and the driver unregistered and unloaded from
the kernel. But the tty core and tty_driver
structure do not provide a read function; in other words; no function
callback exists to get data from the driver to the tty core.
Instead of a conventional
read function, the tty driver is
responsible for sending any data received from the hardware to the
tty core when it is received. The tty core buffers the data until it
is asked for by the user. Because of the buffering logic the tty core
provides, it is not necessary for every tty driver to implement its
own buffering logic. The tty core notifies the tty driver when a user
wants the driver to stop and start sending data, but if the internal
tty buffers are full, no such notification occurs.
The tty core buffers the data received by the tty drivers in a
structure called struct
tty_flip_buffer. A flip buffer is a structure that
contains two main data arrays. Data being received from the tty
device is stored in the first array. When that array is full, any
user waiting on the data is notified that data is available to be
read. While the user is reading the data from this array, any new
incoming data is being stored in the second array. When that array is
finished, the data is again flushed to the user, and the driver
starts to fill up the first array. Essentially, the data being
received "flips" from one buffer to
the other, hopefully not overflowing both of them. To try to prevent
data from being lost, a tty driver can monitor how big the incoming
array is, and, if it fills up, tell the tty driver to flush the
buffer at this moment in time, instead of waiting for the next
available chance.
The details of the
struct
tty_flip_buffer structure do not really matter to
the tty driver, with one exception, the variable
count. This variable contains how many bytes are
currently left in the buffer that are being used for receiving data.
If this value is equal to the value
TTY_FLIPBUF_SIZE, the flip buffer needs to be
flushed out to the user with a call to
tty_flip_buffer_push. This is shown in the
following bit of code:
for (i = 0; i < data_size; ++i) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], TTY_NORMAL);
}
tty_flip_buffer_push(tty);
Characters that are received from the tty driver to be sent to the
user are added to the flip buffer with a call to
tty_insert_flip_char. The first parameter of
this function is the struct
tty_struct the data should be saved in, the second
parameter is the character to be saved, and the third parameter is
any flags that should be set for this character. The flags value
should be set to TTY_NORMAL if this is a normal
character being received. If this is a special type of character
indicating an error receiving data, it should be set to
TTY_BREAK, TTY_FRAME,
TTY_PARITY, or TTY_OVERRUN,
depending on the error.
In order to "push" the data to the
user, a call to tty_flip_buffer_push is made.
This function should also be called if the flip buffer is about to
overflow, as is shown in this example. So whenever data is added to
the flip buffer, or when the flip buffer is full, the tty driver must
call tty_flip_buffer_push. If the tty driver can
accept data at very high rates, the
tty->low_latency flag should be set, which
causes the call to tty_flip_buffer_push to be
immediately executed when called. Otherwise, the
tty_flip_buffer_push call schedules itself to
push the data out of the buffer at some later point in
the near future.
|