aboutsummaryrefslogtreecommitdiff
path: root/documentation/content/zh-cn/books/arch-handbook/driverbasics/chapter.adoc
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/content/zh-cn/books/arch-handbook/driverbasics/chapter.adoc')
-rw-r--r--documentation/content/zh-cn/books/arch-handbook/driverbasics/chapter.adoc501
1 files changed, 0 insertions, 501 deletions
diff --git a/documentation/content/zh-cn/books/arch-handbook/driverbasics/chapter.adoc b/documentation/content/zh-cn/books/arch-handbook/driverbasics/chapter.adoc
deleted file mode 100644
index 8175cbfb6d..0000000000
--- a/documentation/content/zh-cn/books/arch-handbook/driverbasics/chapter.adoc
+++ /dev/null
@@ -1,501 +0,0 @@
----
-title: 第 9 章 编写 FreeBSD 设备驱动程序
-prev: books/arch-handbook/smp
-next: books/arch-handbook/isa
----
-
-[[driverbasics]]
-= 编写 FreeBSD 设备驱动程序
-:doctype: book
-:toc: macro
-:toclevels: 1
-:icons: font
-:sectnums:
-:source-highlighter: rouge
-:experimental:
-:skip-front-matter:
-:toc-title: 目录
-:part-signifier: 部分
-:appendix-caption: 附录
-:table-caption: 表
-:figure-caption: 图
-:example-caption: 例
-:xrefstyle: basic
-:relfileprefix: ../
-:outfilesuffix:
-:sectnumoffset: 9
-
-include::shared/mirrors.adoc[]
-include::shared/authors.adoc[]
-include::shared/releases.adoc[]
-include::shared/zh-cn/mailing-lists.adoc[]
-include::shared/zh-cn/teams.adoc[]
-include::shared/zh-cn/urls.adoc[]
-
-toc::[]
-
-[[driverbasics-intro]]
-== 简介
-
-本章简要介绍了如何为FreeBSD编写设备驱动程序。术语设备在 这儿的上下文中多用于指代系统中硬件相关的东西,如磁盘,打印机, 图形显式器及其键盘。设备驱动程序是操作系统中用于控制特定设备的 软件组件。也有所谓的伪设备,即设备驱动程序用软件模拟设备的行为, 而没有特定的底层硬件。设备驱动程序可以被静态地编译进系统,或者 通过动态内核链接工具'``kld``'在需要时加载。
-
-类UNIX(R)操作系统中的大多数设备都是通过设备节点来访问的,有时也 被称为特殊文件。这些文件在文件系统的层次结构中通常位于 [.filename]##/dev##目录下。在FreeBSD 5.0-RELEASE以前的 发行版中, 对man:devfs[5]的支持还没有被集成到FreeBSD中,每个设备 节点必须要静态创建,并且独立于相关设备驱动程序的存在。系统中大 多数设备节点是通过运行``MAKEDEV``创建的。
-
-设备驱动程序可以粗略地分为两类,字符和网络设备驱动程序。
-
-[[driverbasics-kld]]
-== 动态内核链接工具-KLD
-
-kld接口允许系统管理员从运行的系统中动态地添加和删除功能。 这允许设备驱动程序的编写者将他们的新改动加载到运行的内核中, 而不用为了测试新改动而频繁地重启。
-
-kld接口通过下面的特权命令使用:
-
-* `kldload` - 加载新内核模块
-* `kldunload` - 卸载内核模块
-* `kldstat` - 列举当前加载的模块
-
-内核模块的程序框架
-
-[.programlisting]
-....
-/*
- * KLD程序框架
- * 受Andrew Reiter在Daemonnews上的文章所启发
- */
-
-#include sys/types.h
-#include sys/module.h
-#include sys/systm.h /* uprintf */
-#include sys/errno.h
-#include sys/param.h /* kernel.h中用到的定义 */
-#include sys/kernel.h /* 模块初始化中使用的类型 */
-
-/*
- * 加载处理函数,负责处理KLD的加载和卸载。
- */
-
-static int
-skel_loader(struct module *m, int what, void *arg)
-{
- int err = 0;
-
- switch (what) {
- case MOD_LOAD: /* kldload */
- uprintf("Skeleton KLD loaded.\n");
- break;
- case MOD_UNLOAD:
- uprintf("Skeleton KLD unloaded.\n");
- break;
- default:
- err = EOPNOTSUPP;
- break;
- }
- return(err);
-}
-
-/* 向内核其余部分声明此模块 */
-
-static moduledata_t skel_mod = {
- "skel",
- skel_loader,
- NULL
-};
-
-DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY);
-....
-
-=== Makefile
-
-FreeBSD提供了一个makefile包含文件,利用它你可以快速地编译 你附加到内核的东西。
-
-[.programlisting]
-....
-SRCS=skeleton.c
-KMOD=skeleton
-
-.include bsd.kmod.mk
-....
-
-简单地用这个makefile运行``make``就能够创建文件 [.filename]#skeleton.ko#,键入如下命令可以把它加载到内核:
-
-[source,shell]
-....
-# kldload -v ./skeleton.ko
-....
-
-[[driverbasics-access]]
-== 访问设备驱动程序
-
-UNIX(R) 提供了一套公共的系统调用供用户的应用程序使用。当用户访问 设备节点时,内核的上层将这些调用分发到相应的设备驱动程序。脚本 ``/dev/MAKEDEV``为你的系统生成了大多数的设备节点, 但如果你正在开发你自己的驱动程序,可能需要用 ``mknod``创建你自己的设备节点。
-
-=== 创建静态设备节点
-
-``mknod``命令需要四个参数来创建设备节点。 你必须指定设备节点的名字,设备的类型,设备的主号码和设备的从号码。
-
-=== 动态设备节点
-
-设备文件系统,或者说devfs,在全局文件系统名字空间中提供对 内核设备名字空间的访问。这消除了由于有设备驱动程序而没有静态 设备节点,或者有设备节点而没有安装设备驱动程序而带来的潜在问题。 Devfs仍在进展中,但已经能够工作得相当好了。
-
-[[driverbasics-char]]
-== 字符设备
-
-字符设备驱动程序直接从用户进程传输数据,或传输数据到用户进程。 这是最普通的一类设备驱动程序,源码树中有大量的简单例子。
-
-这个简单的伪设备例子会记住你写给它的任何值,并且当你读取它的时候 会将这些值返回给你。下面显示了两个版本,一个适用于FreeBSD 4.X, 一个适用于FreeBSD 5.X。
-
-.适用于FreeBSD 4.X的回显伪设备驱动程序实例
-[example]
-====
-[.programlisting]
-....
-/*
- * 简单‘echo’伪设备KLD
- *
- * Murray Stokely
- */
-
-#define MIN(a,b) (((a) (b)) ? (a) : (b))
-
-#include sys/types.h
-#include sys/module.h
-#include sys/systm.h /* uprintf */
-#include sys/errno.h
-#include sys/param.h /* kernel.h中用到的定义 */
-#include sys/kernel.h /* 模块初始化中使用的类型 */
-#include sys/conf.h /* cdevsw结构 */
-#include sys/uio.h /* uio结构 */
-#include sys/malloc.h
-
-#define BUFFERSIZE 256
-
-/* 函数原型 */
-d_open_t echo_open;
-d_close_t echo_close;
-d_read_t echo_read;
-d_write_t echo_write;
-
-/* 字符设备入口点 */
-static struct cdevsw echo_cdevsw = {
- echo_open,
- echo_close,
- echo_read,
- echo_write,
- noioctl,
- nopoll,
- nommap,
- nostrategy,
- "echo",
- 33, /* 为lkms保留 - /usr/src/sys/conf/majors */
- nodump,
- nopsize,
- D_TTY,
- -1
-};
-
-typedef struct s_echo {
- char msg[BUFFERSIZE];
- int len;
-} t_echo;
-
-/* 变量 */
-static dev_t sdev;
-static int count;
-static t_echo *echomsg;
-
-MALLOC_DECLARE(M_ECHOBUF);
-MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module");
-
-/*
- * 这个函数被kld[un]load(2)系统调用来调用,
- * 以决定加载和卸载模块时需要采取的动作。
- */
-
-static int
-echo_loader(struct module *m, int what, void *arg)
-{
- int err = 0;
-
- switch (what) {
- case MOD_LOAD: /* kldload */
- sdev = make_dev(echo_cdevsw,
- 0,
- UID_ROOT,
- GID_WHEEL,
- 0600,
- "echo");
- /* kmalloc分配供驱动程序使用的内存 */
- MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK);
- printf("Echo device loaded.\n");
- break;
- case MOD_UNLOAD:
- destroy_dev(sdev);
- FREE(echomsg,M_ECHOBUF);
- printf("Echo device unloaded.\n");
- break;
- default:
- err = EOPNOTSUPP;
- break;
- }
- return(err);
-}
-
-int
-echo_open(dev_t dev, int oflags, int devtype, struct proc *p)
-{
- int err = 0;
-
- uprintf("Opened device \"echo\" successfully.\n");
- return(err);
-}
-
-int
-echo_close(dev_t dev, int fflag, int devtype, struct proc *p)
-{
- uprintf("Closing device \"echo.\"\n");
- return(0);
-}
-
-/*
- * read函数接受由echo_write()存储的buf,并将其返回到用户空间,
- * 以供其他函数访问。
- * uio(9)
- */
-
-int
-echo_read(dev_t dev, struct uio *uio, int ioflag)
-{
- int err = 0;
- int amt;
-
- /*
- * 这个读操作有多大?
- * 与用户请求的大小一样,或者等于剩余数据的大小。
- */
- amt = MIN(uio-uio_resid, (echomsg-len - uio-uio_offset 0) ?
- echomsg-len - uio-uio_offset : 0);
- if ((err = uiomove(echomsg-msg + uio-uio_offset,amt,uio)) != 0) {
- uprintf("uiomove failed!\n");
- }
- return(err);
-}
-
-/*
- * echo_write接受一个字符串并将它保存到缓冲区,用于以后的访问。
- */
-
-int
-echo_write(dev_t dev, struct uio *uio, int ioflag)
-{
- int err = 0;
-
- /* 将字符串从用户空间的内存复制到内核空间 */
- err = copyin(uio-uio_iov-iov_base, echomsg-msg,
- MIN(uio-uio_iov-iov_len, BUFFERSIZE - 1));
-
- /* 现在需要以null结束字符串,并记录长度 */
- *(echomsg-msg + MIN(uio-uio_iov-iov_len, BUFFERSIZE - 1)) = 0;
- echomsg-len = MIN(uio-uio_iov-iov_len, BUFFERSIZE);
-
- if (err != 0) {
- uprintf("Write failed: bad address!\n");
- }
- count++;
- return(err);
-}
-
-DEV_MODULE(echo,echo_loader,NULL);
-....
-
-====
-
-.适用于FreeBSD 5.X回显伪设备驱动程序实例
-[example]
-====
-[.programlisting]
-....
-/*
- * 简单‘echo’伪设备 KLD
- *
- * Murray Stokely
- *
- * 此代码由Søren (Xride) Straarup转换到5.X
- */
-
-#include sys/types.h
-#include sys/module.h
-#include sys/systm.h /* uprintf */
-#include sys/errno.h
-#include sys/param.h /* kernel.h中用到的定义 */
-#include sys/kernel.h /* 模块初始化中使用的类型 */
-#include sys/conf.h /* cdevsw结构 */
-#include sys/uio.h /* uio结构 */
-#include sys/malloc.h
-
-#define BUFFERSIZE 256
-
-/* 函数原型 */
-static d_open_t echo_open;
-static d_close_t echo_close;
-static d_read_t echo_read;
-static d_write_t echo_write;
-
-/* 字符设备入口点 */
-static struct cdevsw echo_cdevsw = {
- .d_version = D_VERSION,
- .d_open = echo_open,
- .d_close = echo_close,
- .d_read = echo_read,
- .d_write = echo_write,
- .d_name = "echo",
-};
-
-typedef struct s_echo {
- char msg[BUFFERSIZE];
- int len;
-} t_echo;
-
-/* 变量 */
-static struct cdev *echo_dev;
-static int count;
-static t_echo *echomsg;
-
-MALLOC_DECLARE(M_ECHOBUF);
-MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module");
-
-/*
- * 这个函数被kld[un]load(2)系统调用来调用,
- * 以决定加载和卸载模块时需要采取的动作.
- */
-
-static int
-echo_loader(struct module *m, int what, void *arg)
-{
- int err = 0;
-
- switch (what) {
- case MOD_LOAD: /* kldload */
- echo_dev = make_dev(echo_cdevsw,
- 0,
- UID_ROOT,
- GID_WHEEL,
- 0600,
- "echo");
- /* kmalloc分配供驱动程序使用的内存 */
- echomsg = malloc(sizeof(t_echo), M_ECHOBUF, M_WAITOK);
- printf("Echo device loaded.\n");
- break;
- case MOD_UNLOAD:
- destroy_dev(echo_dev);
- free(echomsg, M_ECHOBUF);
- printf("Echo device unloaded.\n");
- break;
- default:
- err = EOPNOTSUPP;
- break;
- }
- return(err);
-}
-
-static int
-echo_open(struct cdev *dev, int oflags, int devtype, struct thread *p)
-{
- int err = 0;
-
- uprintf("Opened device \"echo\" successfully.\n");
- return(err);
-}
-
-static int
-echo_close(struct cdev *dev, int fflag, int devtype, struct thread *p)
-{
- uprintf("Closing device \"echo.\"\n");
- return(0);
-}
-
-/*
- * read函数接受由echo_write()存储的buf,并将其返回到用户空间,
- * 以供其他函数访问。
- * uio(9)
- */
-
-static int
-echo_read(struct cdev *dev, struct uio *uio, int ioflag)
-{
- int err = 0;
- int amt;
-
- /*
- * 这个读操作有多大?
- * 等于用户请求的大小,或者等于剩余数据的大小。
- */
- amt = MIN(uio-uio_resid, (echomsg-len - uio-uio_offset 0) ?
- echomsg-len - uio-uio_offset : 0);
- if ((err = uiomove(echomsg-msg + uio-uio_offset, amt, uio)) != 0) {
- uprintf("uiomove failed!\n");
- }
- return(err);
-}
-
-/*
- * echo_write接受一个字符串并将它保存到缓冲区, 用于以后的访问.
- */
-
-static int
-echo_write(struct cdev *dev, struct uio *uio, int ioflag)
-{
- int err = 0;
-
- /* 将字符串从用户空间的内存复制到内核空间 */
- err = copyin(uio-uio_iov-iov_base, echomsg-msg,
- MIN(uio-uio_iov-iov_len, BUFFERSIZE - 1));
-
- /* 现在需要以null结束字符串,并记录长度 */
- *(echomsg-msg + MIN(uio-uio_iov-iov_len, BUFFERSIZE - 1)) = 0;
- echomsg-len = MIN(uio-uio_iov-iov_len, BUFFERSIZE);
-
- if (err != 0) {
- uprintf("Write failed: bad address!\n");
- }
- count++;
- return(err);
-}
-
-DEV_MODULE(echo,echo_loader,NULL);
-....
-
-====
-
-在FreeBSD 4.X上安装此驱动程序,你将首先需要用如下命令在 你的文件系统上创建一个节点:
-
-[source,shell]
-....
-# mknod /dev/echo c 33 0
-....
-
-驱动程序被加载后,你应该能够键入一些东西,如:
-
-[source,shell]
-....
-# echo -n "Test Data" > /dev/echo
-# cat /dev/echo
-Test Data
-....
-
-真正的硬件设备在下一章描述。
-
-补充资源
-
-* http://ezine.daemonnews.org/200010/blueprints.html[Dynamic Kernel Linker (KLD) Facility Programming Tutorial] - http://www.daemonnews.org/[Daemonnews] October 2000
-* http://ezine.daemonnews.org/200007/newbus-intro.html[How to Write Kernel Drivers with NEWBUS] - http://www.daemonnews.org/[Daemonnews] July 2000
-
-[[driverbasics-block]]
-== 块设备(消亡中)
-
-其他UNIX(R)系统支持另一类型的磁盘设备,称为块设备。块设备是内核 为它们提供缓冲的磁盘设备。这种缓冲使得块设备几乎没有用,或者说非常 不可靠。缓冲会重新安排写操作的次序,使得应用程序丧失了在任何时刻及时 知道准确的磁盘内容的能力。这导致对磁盘数据结构(文件系统,数据库等)的 可预测的和可靠的崩溃恢复成为不可能。由于写操作被延迟,内核无法向应用 程序报告哪个特定的写操作遇到了写错误,这又进一步增加了一致性问题。 由于这个原因,真正的应用程序从不依赖于块设备,事实上,几乎所有访问 磁盘的应用程序都尽力指定总是使用字符(或"raw")设备。 由于实现将每个磁盘(分区)同具有不同语义的两个设备混为一谈,从而致使 相关内核代码极大地复杂化,作为推进磁盘I/O基础结构现代化的一部分,FreeBSD 抛弃了对带缓冲的磁盘设备的支持。
-
-[[driverbasics-net]]
-== 网络设备驱动程序
-
-访问网络设备的驱动程序不需要使用设备节点。选择哪个驱动程序是 基于内核内部的其他决定而不是调用open(),对网络设备的使用通常由 系统调用socket(2)引入。
-
-更多细节, 请参见 ifnet(9) 联机手册、 回环设备的源代码, 以及 Bill Paul 撰写的网络驱动程序。