Chinaunix首页 | 论坛 | 博客
  • 博客访问: 274188
  • 博文数量: 74
  • 博客积分: 2811
  • 博客等级: 少校
  • 技术积分: 710
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-02 21:14
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(24)

2009年(49)

我的朋友

分类:

2009-06-02 21:45:40

lDD LINUX DEVICE DRIVER
机制与策略
有什么样的功能,如何运用这些功能

设备分成3类,字符,块,网络。

字符设备
一个字符( char ) 设备是一种可以当作一个字节流来存取的设备( 如同一个文件 ); 一个字符
驱动负责实现这种行为. 这样的驱动常常至少实现 open, close, read, 和 write 系统调用.
字符设备通过文件系统结点来存取

块设备
块设备通过位于 /dev 目录的文件系统结点来存取
与字符设备区别:块和字符设备的区别仅仅在内核在内部管理数据的方式上, 并且因此在内核/驱动的软件接口上不同.

网络设备
网络设备常常设计成处理报文的发送和接收. 一个网络驱动对单个连接一无所知; 它只处理报文.
 
 
HELLOWORLD模块
#include
#include
MODULE_LICENSE("DualBSD/GPL");
static int hello_init(void)
{
 printk(KERN_ALERT"hello_world");
}
static void hello_exit(void)
{
 printk(KERN_ALERT"hello_exit");
}
module_init(hello_init);
module_exit(hello_exit);
其中包含了2个函数,编译后可以用insmod和rmmod加载和卸载模块
 
makefile的内容
KERNELDIR = /home/flying/qq2440/kernel/linux-2.6.13
PWD := $(shell pwd)
CROSS_COMPILE = arm-linux-
CC = $(CROSS_COMPILE)gcc
obj-m := hello.o
modules:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
 rm -rf *.o *~core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers
.PHONE :modules modules_install clean
 
编译和加载
编译前提:编译工具,内核树并且用编译工具编译过,编译器不能太新也不能太久
MAKEFILE的编写:对上面的makefile进行讲解
KERNELDIR = /home/flying/qq2440/kernel/linux-2.6.13
#内核的目录
PWD := $(shell pwd)
#当前目录
CROSS_COMPILE = arm-linux-
#编译的工具
CC = $(CROSS_COMPILE)gcc
obj-m := hello.o
#要编译的文件
modules:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
#-C 转到KERNELDIR的目录下,M 选项使 makefile 在试图建立模块目标前, 回到你的模块源码目
clean:
#清除所有make后产生的文件
 
加载和卸载
加载:insmod或modprobe
卸载:rmmod
列表:lsmod
rmmod卸载正在运行的或不允许卸载的模块时会报错
lsmod是调用/proc/modules的虚拟文件,也可以用/sys/modules来查看
 
版本依赖
不同版本的内核配置不同版本的模块,更换了内核版本后模块要重新编译
 
内核符号表
如果其他模块要使用这本模块的某个函数,则需要这样定义
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);
 
预备知识:
模块必须加入的头文件:
#include
#include
例如如果要在加载模块的时候加入参数设置的,则要加入moduleparam.h
 
还要加入许可协议:
MODULE_LICENSE("GPL")
 
还有一些相关的说明:
MODULE_AUTHOR()
MODULE_DESCRIPION()
 
模块初始化:
使用MODULE_INIT()来说明初始化的函数名,这个是必须的。。。
例如:
static int hello_init(void)
MODULE_INIT(hello_init)
模块清除:
使用MODULE_EXIT()来退出模块,由于模块的没有返回值,所以定义的函数用VOID
static void hello_exit(void)
MODULE_EXIT(hello_exit)
撤销的顺序一般为注册函数的反顺序。。。。
如果没有设置退出函数,则模块是不能退出的
 
初始化失败的处理:
如果模块初始化失败,则必须能退回到所有东西,注销你已经注册了的东西。。如果不能注销,着内核将处在不稳定状态之下。。。
如果初始化失败,可以使用goto来处理,但使用的时候必须小心。。。
 
使用例子:
int __init my_init_function(void)
{
        int err;
        /* registration takes a pointer and a name */
        err = register_this(ptr1, "skull");
        if (err)
                goto fail_this;
        err = register_that(ptr2, "skull");
        if (err)
                goto fail_that;
        err = register_those(ptr3, "skull");
        if (err)
                goto fail_those;
        return 0; /* success */
fail_those:
        unregister_that(ptr2, "skull");
fail_that:
        unregister_this(ptr1, "skull");
fail_this:
        return err; /* propagate the error */
}
 
除了GOTO,还有用回卷的方式返回已经成功的步骤上,但这样比较消耗CPU和时间
 
err在LINUX里面一般为负数,如果想要自定义,着需要引入以便使用符号返回值-ENODEV, -ENOMEM
 
模块参数
我们在原来的基础上增加参数“howmany","whom"两个参数
howmany用于重复的次数为整型变量,whom用于打印谁为字符变量
然后用insmod hellop howmany=10 whom="Mom"
 
编写的模块函数需要增加头文件linux/moduleparam.h
而moduleparam函数需要输入的参数为参数名,参数类型,权限值
static char *whom = "world";
static int howmany = 1;
module_param(howmany,int,S_IRUGO);
module_param(whom,char,S_IRUGO);
 
其中参数类型支持bool,invbool,charp,int,long,short,uint,ulong,ushort
参数的权限值使用linux/stat.h里面定义的权限名(补充权限值的定义)
 
还可以申请一个数组参数类型,定义如下
module_param_array(name,type,num,perm);
name为参数名 type为参数类型 num为参数个数 perm为参数权限
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
阅读(548) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:数字电视业务信息及其编码

给主人留下些什么吧!~~