Poster of Linux kernelThe best gift for a Linux geek
 Linux kernel map 
⇦ prev ⇱ home next ⇨

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.

    ⇦ prev ⇱ home next ⇨
    Poster of Linux kernelThe best gift for a Linux geek