- 论坛徽章:
- 0
|
试着翻译一下,刚刚学习,错误或不妥的地方还请大家指正。附上文件,大家可以直接修改我翻译不准确或错误的地方,然后再拿出来共享
原文 http://www.netbsd.org/Documentation/kernel/pseudo/
NetBSD文档:如何编写一个伪设备
简介
本文是为那些想学习写内核驱动的人的入门指南,全文围绕一个简单的伪设备展开。阅读本文你需要熟悉内核的编译、makefile、安装内核这些本文没涉及的内容。本文也不涉及内核编程本身,它和应用编程相当不同。总之,本文是你进入内核编程世界必经之路。
你的代码
pseudo_dev_skel.c文件给出了伪设备的框架,pseudo_dev_skel.h定义了内核函数原型和ioctl数据结构以及ioctl号。注意,不同于一般的驱动,伪设备没有常规的探测过程,因为这没必要,这简化了开发过程因为我们不必和autoconfig框架打交道。我们提供了一个支持open、close和ioctl调用的例子,这是一个真正的伪设备最小的功能集。一般伪设备还会有read、write、mmap和其他的设备函数,但它们都和open、close和ioctl一样遵循同样的模式,因此,本文就省略了这些内容。
也许你需要弄清的首要任务是怎么称呼你的新设备。有很多方便的用来生成内核结构的宏可以把设备名和函数调用名联系起来,如果你要在内核配置文件中添加一项的话这些宏就会派上用场,配置文件项并不指定头文件名。在我们的示例驱动中我们管这个为设备叫“skeleton”,所以我们得有一个叫skeleton的配置文件项。这意味着attach、open、close和ioctl函数以skeletonattach、skeletonopen、skeletonclose和skeletonioctl的名字被调用。另一个问题是,你写的伪设备是块设备还是字符设备,这将影响到你的代码和内核的交互,毕竟是你自己写这些代码。写设备驱动就面临着块设备驱动还是字符设备驱动的问题,这取决于它们跟什么设备通信,根块设备通信就最好选块设备,反之亦然。有些设备即支持块模式又支持字符模式,有时称之为“raw”设备,因为它不提供数据块抽象操作。伪设备就没有这个问题,根本不必考虑硬件,而根据它的用途来确定,模拟磁盘就选块设备驱动,但是我们的例子是个字符设备。
一旦问题确定了我们就开始弄代码了,在此之前我们要决定代码放在哪里,如果我们的伪设备要在多平台下使用就放在/usr/src/sys/dev,如果只在x86下用就放在/usr/src/sys/arch/i386/的相应目录下。pseudo_dev_skel.c的头部应该是skeleton_softc结构,<你的设备名>_softc这样的softc结构是必须的,其第一个成员必须是 struct device类型,entry的名字不重要,但是必须是第一个,因为autoconfig读取softc结构而不加检查,并且必须是一个struct device。每个softc对应一个设备的子设备号,如果子设备需要保存状态信息的话softc结构可以比struct device保存更多的成员。
函数
你的设备的内核接口可以通过一组函数来被用户态程序访问。一个设备不需要支持全部接口,但是一个有用的设备最少要支持open和close。记住这些函数和你的设备被联系起来了。
1. attach()
该函数在内核初始化时被调用一次,被用来设置所有被稍后用于分配需要的用作缓冲的内核空间的调用所引用的变量。该函数只有一个参数就是要操纵的驱动的设备号。
2. open()
打开设备。简单的情况会返回成功,一般还要检查状态确认是否成功。该函数有四个参数:
dev
要操纵的设备的子设备号。
flags
就是应用编程中open的flags参数
mode
打开的模式
proc
指向打开设备的进程的指针,它允许被进程确认。
3. close()
关闭设备。也可以是简单的返回成功、释放先前非配的内存、更新驱动状态等等。和open的参数一样。
4. read()
读数据。参数:
dev
子设备号。
uio
指向uio结构的指针,read会用它要返回给用户的数据填uio结构。
flags
就是flags
5. write()
写。参数和read一样,不同就是要写的数据填uio结构。
6. ioctl()
I/O控制命令。参数:
dev
子设备号
cmd
命令,命令在内核代码和用户态代码共同的头文件中定义。参见sample header。
data
指向用户态代码传过来的参数,在该参数的内容由ioctl的实现决定。
7. stop()
如果是tty设备,在tty设备上停止输出。参数:
tty
和tty相关联的设备
flags
就是flags
8. poll()
通过读数据来检查设备,轮询,参数:
dev
子设备号
events
来自用户态代的调用轮询的事件
proc
指向ioctl要求的用户态进程的指针
9. mmap()
支持高性能的把驱动缓冲映射到用户态程序的地址空间中去,参数是:
dev
设备的子设备号
offset
开始映射的地方,从缓冲区开始算的偏移量
port
mmap的类型,只读还是读写,设备驱动并不支持所有的类型
你的设备驱动支持的函数的名称必须被加到<你的模块名>_cdevsw那个struct cdevsw中去(如果是字符设备,块设备是struct bdevsw),本例是skeleton_cdevsw。注意:这些结构中包含所有的接口而你只须实现一部分。与其强迫所有人实现不必要的函数不如放一些预定义的插桩函数,当no函数(如noread、nowrite等)被调用时,它们返回ENODEV或null(nullread、nullwrite等),但是预定义的插桩函数会返回成功但是什么也不做,你的设备不必支持块设备和字符设备的这些预定义的插桩函数。
向内核添加你的设备
一旦你写好了你的伪设备,就要hook进内核测试一下了。加载伪设备和实际设备不同,伪设备没有探测和自动配置的过程,让内核使用你的伪设备要修改下面这些文件:
1. /usr/src/sys/conf/majors或/usr/src/sys/<arch>/conf/majors.<arch>
这些文件包含NetBSD的主设备号列表,/usr/src/sys/conf/majors包含机器无关的主设备号列表,某特殊平台的在/usr/src/sys/<arch>/conf/majors.<arch>中。它们以下面的形式被列出:
device-major prefix type number condition
这些符号的意思详见config(5)
device-major
标示主设备号的关键字
prefix
前缀,被自动加到所有函数的前面,如本例的skeleton
type
主设备的类型,char还是block,你可以通过重复type/number来制定一个既是块设备又是字符设备的设备
number
主设备号,选下一个可用的数字(当前设备用了1-8你就用9),你需要在/dev下为它创建一个设备节点
condition
设备被包含进内核的条件,需要和你在内核配置文件(下面讲)的描述一致。
因为我们的skeleton示例是个字符设备,并只工作在i386下,我们把下面一行添到/usr/src/sys/arch/i386/conf/majors.i386中去,这里主设备号是140。
device-major skeleton char 140 skeleton
向config(1)添加你的设备
让config(1)认识你的设备我们需要修改/usr/src/sys/conf/files(机器无关的设备)或/usr/src/sys/arch/<arch>/conf/files.<arch>。这些文件告诉config活跃的设备的名字和那些设备和哪个文件相关联。首先我们找到伪设备的定义那一节,以defpseudo开头,本例中我们需要修改/usr/src/sys/arch/i386/conf/files.i386,找到
这一行告诉config(1)我们有个叫skeleton的伪设备接下来需要告诉config(1)的是skeleton和哪个文件关联,我们只需加入一行就行:
file dev/skeleton.c skeleton needs-flag
file是关键字,dev/skeleton.c就是那个文件(通常在/usr/src/sys),skeleton是驱动的名字,needs-flag告诉config(1)生成skeleton.h文件。注意:在这里第二项的文件名和我们的文件名是对应相同的。
向内核配置文件添加新设备
一旦config(1)被告知了有个设备,向内核配置文件添加设备就简单了,加一行就行:
在内核配置文件中pseudo-device 这一行和前面的defpseudo那一行对应。新的定义可以通过option关键字加入内核,配置会产生一个makefile,给option的项的cc命令加-D参数。
使用户态程序可以访问你的设备
在编译和安装新内核之后,就需要为设备新建一个设备节点,你可以在文件系统你可以访问的任何地方创建节点,但一般是在/etc下。创建设备节点你只需打这么一个mknod( 8 )命令:
# mknod /dev/skel c 140 0
一旦你创建了节点就可以打开它测试一下。sample.c就是测试文件。下面的命令编译它:
会得到一个叫sample的二进制文件。注意:你必须已经在编译目录运行过make includes,即把你的pseudo_dev_skel.h拷贝到了系统的includes目录下。你运行sample可以在终端和/var/log/messages查看内核消息。消息是这个样子的:
May 17 20:32:57 siren /netbsd: Got number of 42 and string of Hello World
[ 本帖最后由 prolj 于 2007-11-29 15:36 编辑 ] |
|