HyperNews Linux KHG 讨论页面

字符设备驱动程序

初始化

除了由file_operations结构定义的函数之外,至少还有一个您必须编写的函数,即foo_init()函数。您将需要更改chr_dev_init()在 drivers/char/mem.c 中调用您的foo_init()函数。

foo_init()应该首先调用register_chrdev()来注册它自己并避免设备号冲突。register_chrdev()接受三个参数

int major
这是驱动程序希望分配的主设备号。
char *name
这是驱动程序的符号名称。除其他事项外,这用于在 /proc 文件系统中报告驱动程序的名称。
struct file_operations *f_ops
这是您的file_operations结构的地址。
返回值
如果没有任何其他字符设备注册了相同的主设备号,则返回 0。
如果调用失败,则返回非 0 值,这可能是因为另一个字符设备已经分配了该主设备号。

通常,foo_init()例程将尝试检测它应该驱动的硬件。它应该确保为所有存在的硬件填充所有必要的数据结构,并采取某种方法确保不会访问不存在的硬件。 [详细说明不同的方法。特别是,文档化 request_* 和相关函数。]

中断 vs. 轮询

在轮询驱动程序中,foo_read()foo_write()函数很容易编写。这是一个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()foo_handle_error()是 foo.c 中其他地方定义的函数或伪代码。WRITE将是一个常量或#define.

从这个例子中应该清楚如何编写foo_read()函数。

中断驱动的驱动程序稍微困难一些。这是一个中断驱动的foo_write()的例子

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);
}

同样,foo_read()函数以类似的方式编写。foo_table[]是一个结构数组,每个结构都有几个成员,其中一些是foo_wait_queuebytes_xfered,可以用于读取和写入。foo_irq[]是一个包含 16 个整数的数组,用于查找foo_table[]中哪个条目与irq关联,该中断被生成并报告给foo_interrupt()函数。

要告诉中断处理代码调用foo_interrupt(),您需要使用request_irq()irqaction()。这可以在调用foo_open()时完成,或者如果您想保持简单,则在foo_init()调用时完成。request_irq()是两者中较简单的一个,并且很像旧式信号处理程序。它接受两个参数:第一个是您请求的irq的数量,第二个是指向您的中断处理程序的指针,该处理程序必须接受一个整数参数(生成的 irq)并且具有返回类型void. request_irq()返回-EINVAL如果irq> 15 或者如果指向中断处理程序的指针是NULL, -EBUSY如果该中断已被占用,则返回 -EBUSY,成功则返回 0。

irqaction()的功能很像用户级sigaction(),实际上重用了sigaction结构。sigaction 结构的 sa_restorer()字段未使用,但其他所有内容都相同。有关irqaction()的更多信息,请参阅 支持函数 中的条目。irqaction().

版权所有 (C) 1992, 1993, 1994, 1996 Michael K. Johnson, johnsonm@redhat.com。


消息

3. 反馈: 在调用 close 时调用 release() 方法
2. 问题: foo_write(...) 的返回值 作者:My name here
1. 想法: TTY 驱动程序 作者:Daniel Taylor
1. 问题: 是否有任何进展?如果没有... 作者:Andrew Manison