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. Feedback: release() method called when close is called
2. Question: return value of foo_write(...) by My name here
1. Idea: TTY drivers by Daniel Taylor
1. Question: Is anything in the works? If not ... by Andrew Manison