The HyperNews Linux KHG Discussion Pages

### Character Device Drivers

#### Initialization

Besides functions defined by the `file_operations` structure, there is at least one other function that you will have to write, the `foo_init()` function. You will have to change `chr_dev_init()` in drivers/char/mem.c to call your `foo_init()` function.

`foo_init()` should first call `register_chrdev()` to register itself and avoid device number contention. `register_chrdev()` takes three arguments:

`int major`
  This is the major number which the driver wishes to allocate.
`char *name`
  This is the symbolic name of the driver. This is used, among other things, to report the driver's name in the /proc filesystem.
`struct file_operations *f_ops`
  This is the address of your `file_operations` structure.
Returns:
  0 if no other character device has registered with the same major number.
  non-0 if the call fails, presumably because another character device has already allocated that major number.

Generally, the `foo_init()` routine will then attempt to detect the hardware that it is supposed to be driving. It should make sure that all necessary data structures are filled out for all present hardware, and have some way of ensuring that non-present hardware does not get accessed. [Detail different ways of doing this. In particular, document the `request_*` and related functions.]

#### Interrupts vs. Polling

In a polling driver, the `foo_read()` and `foo_write()` functions are pretty easy to write. Here is an example of `foo_write()`:

    static int foo_write(struct inode * inode, struct file * file, char * buf, int count)
    {
        unsigned int minor = MINOR(inode->i_rdev);
        char ret;

        while (count > 0) {
            ret = foo_write_byte(minor);
    	if (ret < 0) {
                foo_handle_error(WRITE, ret, minor);
                continue;
            }
            buf++ = ret; count--
        }
        return count;
    }

`foo_write_byte()` and `foo_handle_error()` are either functions defined elsewhere in foo.c or pseudocode. `WRITE` would be a constant or `#define`.

It should be clear from this example how to code the `foo_read()` function as well.

Interrupt-driven drivers are a little more difficult. Here is an example of a `foo_write()` that is interrupt-driven:

    static int foo_write(struct inode * inode, struct file * file, char * buf, int count)
    {
        unsigned int minor = MINOR(inode->i_rdev);
        unsigned long copy_size;
        unsigned long total_bytes_written = 0;
        unsigned long bytes_written;
        struct foo_struct *foo = &foo_table[minor];

        do {
            copy_size = (count <= FOO_BUFFER_SIZE ? count : FOO_BUFFER_SIZE);
            memcpy_fromfs(foo->foo_buffer, buf, copy_size);

            while (copy_size) {
                /* initiate interrupts */

                if (some_error_has_occured)  {
                    /* handle error condition */
                }

                current->timeout = jiffies + FOO_INTERRUPT_TIMEOUT;
                    /* set timeout in case an interrupt has been missed */
                interruptible_sleep_on(&foo->foo_wait_queue);
                bytes_written = foo->bytes_xfered;
                foo->bytes_written = 0;
                if (current->signal & ~current->blocked) {
                    if (total_bytes_written + bytes_written)
                        return total_bytes_written + bytes_written;
                    else
                        return -EINTR; /* nothing was written, system
                                          call was interrupted, try again */
                }
            }

            total_bytes_written += bytes_written;
            buf += bytes_written;
            count -= bytes_written;

        } while (count > 0);

        return total_bytes_written;
    }

    static void foo_interrupt(int irq)
    {
        struct foo_struct *foo = &foo_table[foo_irq[irq]];

        /* Here, do whatever actions ought to be taken on an interrupt.
           Look at a flag in foo_table to know whether you ought to be
           reading or writing. */

        /* Increment foo->bytes_xfered by however many characters were
           read or written */

        if (buffer too full/empty)
            wake_up_interruptible(&foo->foo_wait_queue);
    }

Again, a `foo_read()` function is written analagously. `foo_table[]` is an array of structures, each of which has several members, some of which are `foo_wait_queue` and `bytes_xfered`, which can be used for both reading and writing. `foo_irq[]` is an array of 16 integers, and is used for looking up which entry in `foo_table[]` is associated with the `irq` generated and reported to the `foo_interrupt()` function.

To tell the interrupt-handling code to call `foo_interrupt()`, you need to use either `request_irq()` or `irqaction()`. This is either done when `foo_open()` is called, or if you want to keep things simple, when `foo_init()` is called. `request_irq()` is the simpler of the two, and works rather like an old-style signal handler. It takes two arguments: the first is the number of the `irq` you are requesting, and the second is a pointer to your interrupt handler, which must take an integer argument (the irq that was generated) and have a return type of `void`. `request_irq()` returns `-EINVAL` if `irq` > 15 or if the pointer to the interrupt handler is `NULL`, `-EBUSY` if that interrupt has already been taken, or 0 on success.

`irqaction()` works rather like the user-level `sigaction()`, and in fact reuses the `sigaction` structure. The `sa_restorer()` field of the sigaction structure is not used, but everything else is the same. See the entry for `irqaction()` in Supporting Functions, for further information about `irqaction()`.

Copyright (C) 1992, 1993, 1994, 1996 Michael K. Johnson, johnsonm@redhat.com.

Messages

3. release() method called when close is called
2. return value of foo_write(...) by My name here
1. TTY drivers by Daniel Taylor
  1. Is anything in the works? If not ... by Andrew Manison