Chinaunix首页 | 论坛 | 博客
  • 博客访问: 348984
  • 博文数量: 88
  • 博客积分: 907
  • 博客等级: 准尉
  • 技术积分: 1230
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-26 13:27
文章分类

全部博文(88)

文章存档

2017年(1)

2014年(3)

2013年(29)

2012年(21)

2011年(26)

2010年(8)

分类: 嵌入式

2010-05-01 22:58:23

 

学习驱动已经有一段时间了!现在将其记载于此!

 

一、开发环境

 

  机:VMWare--Fedora 9

开发板:Micro2440--128MB Nand

编译器:arm-linux-gcc-4.3.2

二、建立和运行模块

 

 

在学习该书的第二章就会遇见hello world 模块

/*这里的代码是来至于Tekkaman  Ninja,刚开始我就用他的代码做测试,自己偷一下懒*/

 

#include

#include

MODULE_LICENSE("Dual BSD/GPL");

 

static int hello_init(void)

{

    printk(KERN_ALERT "Hello, Tekkaman Ninja \n");

    return 0;

}

 

static void hello_exit(void)

{

    printk(KERN_ALERT "Goodbye, Tekkaman Ninja \n Love Linux !Love ARM ! Love KeKe !\n");

}

 

module_init(hello_init);

module_exit(hello_exit);

 

将其复制到我的工作目录,并改写了一个简单的Makefile文件:

 

KERNELDIR = /home/working/S3C2440/linux-2.6.30.4

    # The current directory is passed to sub-makes as argument

PWD := $(shell pwd)

INSTALLDIR = /home/working/modules

 

CROSS_COMPILE    = arm-linux-

CC    = $(CROSS_COMPILE)gcc

 

obj-m := hello.o

 

modules:

    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

 

modules_install:

    cp hello.ko $(INSTALLDIR)

 

clean:

    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

 

.PHONY: modules modules_install clean

 

然后执行make 将会看见如图一所示:

 

 

 

图一
 
 

三、开发板上操作

 /*这里是我重点写的地方*/

1、启动开发板,进入cd /lib/modules/ ;然后在该目录下建立一个文件夹,注意这里的文件夹名字不是乱取的,我的内核是2.6.30.4,所以我只能在该系统下建立一个名字为2.6.30.4的文件夹,操作如图二所示:

 

图二 

 

2、这里出现的情况,你应该知道为什么的,那里已经存在同名文件夹了,所以建立不成功。

 

 

3、因为我只用的是友善的开发板(不是打广告),所以里面可以直接通过rz命令下载模块。在超级终端内执行rz,然后在其内点鼠标右键,你会看见发送文件和接收文件,现在我们选择接收文件。然后通过浏览找到你需要的文件,在我这里就是hello.ko,最后就是点发送。

 
4、在cd /lib/modules/ 下你会看见多了一个文件,就是我们刚才下载的那个文件。在开发板这里需要改变其执行权限,不改变不能执行。
 

 

5、现在来到了测试前边这样做是否是对的关键阶段。我们在这里insmod my_hello_modules.ko,看见输出了语句说明成功,然后执行lsmod看见模块活着,在rmmod my_hello_modules 也看见输出信息。(这里说明一下,有人会觉得奇怪,我前边是用的hello.ko,怎么现在这里用的my_hello_modules.ko,因为我先前的hello.ko不见了,这里这个是做的,你按自己取的名字去建立就好。)

 
 
 

 

四、学习心得 

/*这里的学习心得取之于Tekkaman  Ninja 见他写的很好,所以直接拿来用,有时拿来主义好是不错的*/

 

1)驱动模块运行在内核空间,运行时不能依赖于任何函数库和模块连接,所以在写驱动时所调用的函数只能是作为内核一部分的函数。

2)驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。

3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别。

4)要十分注意驱动程序的并发处理。

5)内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用。

6)内核代码不能实现浮点书运算。

 

7Makefile文件分析:

obj-m := hello.o  代表了我们要构造的模块名为hell.komake 会在该目录下自动找到hell.c文件进行编译。如果 hello.o是由其他的源文件生成(比如file1.cfile2.c)的,则在下面加上(注意红色字体的对应关系):

hello-objs := file1.o file2.o ......

 

    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

其中 -C $(KERNELDIR) 指定了内核源代码的位置,其中保存有内核的顶层makefile文件。

    M=$(PWD) 指定了模块源代码的位置

    modules目标指向obj-m变量中设定的模块。

 

8insmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的。

9Linux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。如果一个模块需要向其他模块到处符号,则使用下面的宏:

 

EXPORT_SYMBOL(name);

EXPORT_SYMBOL_GPL(name);

 

符号必须在模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。

 

10)所有模块代码中都包含一下两个头文件:

 

#include

#include

 

 

11)所有模块代码都应该指定所使用的许可证:

 

MODULE_LICENSE("Dual BSD/GPL");

 

 

 

此外还有可选的其他描述性定义:

 

MODULE_AUTHOR("");

MODULE_DESCRIPTION("");

MODULE_VERSION("");

MODULE_ALIAS("");

MODULE_DEVICE_TABLE("");

 

 

上述MODULE_声明习惯上放在文件最后。

 

12)初始化和关闭

初始化的实际定义通常如下:

 

static int _ _init initialization_function(void)

{

/*初始化代码*/

}

 

module_init(initialization_function)

 

清除函数的实际定义通常如下:

 

static int _ _exit cleanup_function(void)

{

/*清除代码*/

}

 

module_exit(cleanup_function)

 

13 Linux内核模块的初始化出错处理一般使用“goto”语句。通常情况下很少使用“goto”,但在出错处理是(可能是唯一的情况),它却非常有用。学习C语言时,老师就建议不要使用“goto”,并说很少会用到。在这里也是我碰到的第一个建议使用“goto”的地方。“在追求效率的代码中使用goto语句仍是最好的错误恢复机制。”使用的时候要慎用,千万不可乱来--《Linux设备驱动程序(第3版)》以下是初始化出错处理的推荐代码示例:

 

struct something *item1;

struct somethingelse *item2;

int stuff_ok;

 

 

void my_cleanup(void)

{

    if (item1)

 

 

        release_thing(item1);

    if (item2)

        release_thing2(item2);

    if (stuff_ok)

        unregister_stuff();

    return;

}

int __init my_init(void)

{

    int err = -ENOMEM;

    item1 = allocate_thing(arguments);

    item2 = allocate_thing2(arguments2);

    if (!item2 || !item2)

        goto fail;

    err = register_stuff(item1, item2);

    if (!err)

        stuff_ok = 1;

    else

        goto fail;

    return 0; /* success */

 

 

 fail:

        my_cleanup( );

        return err;

}

 

模块参数:内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变。

/*实验程序*/

 

#include

#include

#include

 

MODULE_LICENSE("Dual BSD/GPL");

 

static char *whom = "Tekkaman Ninja";

static int howmany = 1;

static int TNparam[] = {1,2,3,4};

static int TNparam_nr = 4;

module_param(howmany, int, S_IRUGO);

module_param(whom, charp, S_IRUGO);

module_param_array(TNparam , int , &TNparam_nr , S_IRUGO);

 

static int hello_init(void)

{

    int i;

    for (i = 0; i < howmany; i++)

        printk(KERN_ALERT "(%d) Hello, %s \n", i, whom);

    for (i = 0; i < 8; i++)

        printk(KERN_ALERT "TNparam[%d] : %d \n", i, TNparam[i]);

    return 0;

}

 

static void hello_exit(void)

{

    printk(KERN_ALERT "Goodbye, Tekkaman Ninja \n Love Linux !Love ARM ! Love KeKe !\n");

}

 

module_init(hello_init);

module_exit(hello_exit);

 

开发实验结果:

[Tekkaman2440@SBC2440V4]#cd /lib/modules/

[Tekkaman2440@SBC2440V4]#ls

cs89x0.ko hello.ko prism2_usb.ko

hello-param.ko p80211.ko

[Tekkaman2440@SBC2440V4]#insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1

(0) Hello, KeKe

(1) Hello, KeKe

TNparam[0] : 4

TNparam[1] : 3

TNparam[2] : 2

TNparam[3] : 1

TNparam[4] : 1836543848

TNparam[5] : 7958113

TNparam[6] : 1836017783

TNparam[7] : 0

[Tekkaman2440@SBC2440V4]#insmod hello-param.ko howmany=2 whom="KeKe"  TNparam=4,3,2,1,5,6,7,8

TNparam: can only take 4 arguments

hello_param: `4' invalid for parameter `TNparam'

insmod: cannot insert 'hello-param.ko': Invalid parameters (-1): Invalid argument

[Tekkaman2440@SBC2440V4]# 

 

 

这个实验除了对参数的改变进行实验外,另一个重要的目的是测试“ module_param_array(TNparam , int , &TNparam_nr , S_IRUGO);”中&TNparam_nr对输入参数数目的限制作用。经过我的实验,表明&TNparam_nr并没有对输入参数的数目起到限制作用。真正起到限制作用的是“static int TNparam[] = {1,2,3,4};”本身定义的大小,我将程序进行修改:

static int TNparam[] = {1,2,3,4}; 

改为 static int TNparam[] = {1,2,3,4,5,6,7,8};

其他都不变。

 

编译后再进行实验,其结果是:

[Tekkaman2440@SBC2440V4]#insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1,5,6,7,8

(0) Hello, KeKe

(1) Hello, KeKe

TNparam[0] : 4

TNparam[1] : 3

TNparam[2] : 2

TNparam[3] : 1

TNparam[4] : 5

TNparam[5] : 6

TNparam[6] : 7

TNparam[7] : 8

[Tekkaman2440@SBC2440V4]#

 

 

15)“#include   最重要的头文件之一。包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明。

 

16)“#include 包含所构造内核版本信息的头文件。

 

说来惭愧里面很多东西都是取之于:

http://blog.chinaunix.net/u1/34474/showart.php?id=407202

里面只有在开发板的操作的详细步骤是我自己的,以后还得加油啊!

 
 
阅读(918) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:字符设备驱动程序

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