linux驱动学习——第一天(hello world)

        前言:现在所记录的是以前工作学习所留下来的一些断断续续的笔记,现在将它们整理出来,希望能带给到正在学习这方面知道的同学一些帮助。如若发现有误之处,也请不吝指出,不胜感激。

        我们常常听人说Linux操作系统下,一切皆是文件。linux系统主要有3类的设备文件类型:块设备、字符设备和网络设备。那么驱动也主要包括三类设备的驱动,分别是字符设备驱动、块设备驱动以及网络设备驱动。

1、字符设备:是指能够像字节流串行顺序依次进行访问的设备,对它的读写是以字节为单位。字符设备的上层没有磁盘文件系统,所以字符设备的file_operations成员函数就直接由字符设备驱动提供(一般字符设备都会实现相应的fops集),因此file_operations 也就成为了字符设备驱动的核心。
类型举例:鼠标,键盘,串口,LED、GPIO等等.

2、块设备:以数据块的形式存放数据,如NAND Flash以页为单位存储数据,并采用mount方式挂载块设备。
        块设备必须能够随机存取(random access),字符设备则没有这个要求。块设备除了给内核提供和字符设备一样的接口外,还提供了专门面向块设备的接口,块设备的接口必须支持挂装文件系统,通过此接口,块设备能够容纳文件系统,因此应用程序一般通过文件系统来访问块设备上的内容,而不是直接和设备打交道。
        对于块设备而言,上层ext2,jiffs2,fat等文件系统会 实现针对VFS的file_opertations成员函数,所以设备驱动层将看不到file_opeations的存在。磁盘文件系统和设备驱动会将对磁盘上文件的访问转换成对磁盘上柱面和扇区的访问。
类型举例:硬盘,磁盘,U盘,SD卡等等。

3、网络设备在Linux内核中是唯一不体现一切皆设备思想的驱动架构,因为网络设备使用套接字来实现网数据的接受和发送。
       网络设备驱动不同于字符设备和块设备,不在/dev下以文件节点代表,而是通过单独的网络接口来代表。
类型举例:网卡
那么以下我们着重学习的是——  字符设备驱动

一、字符设备驱动之——ko模式驱动开发
    1,基本的四要素:
        a,头文件声明
            #include <linux/init.h>
            #include <linux/module.h>
        b,模块加载函数
            如:static int __init hello_init(void)
        c,模块卸载函数
            如:static void __exit hello_exit(void)
        d,开源声明
            MODULE_LICENSE("GPL");
        当然了,还须要这样声明一下:
        //加载模块函数
        module_init(hello_init);
        //卸载模块函数
        module_exit(hello_exit);

    2,一些小技巧
         //驱动模块作者声明
MODULE_AUTHOR("CHEN JUN");
//驱动版本声明
MODULE_VERSION("V1.0.0");
//本驱动的相关描述
MODULE_DESCRIPTION("This is a driver 'hello' example ");
//传入变量的声明
module_param(age,int,0644);
module_param(str,charp,0644);
        
        说明:以上均为在自己要编写的驱动文件.c  里进行。
依据国际惯例,以一声hello world而得以诞生。以下是我们编写的第一个简单驱动文件hello.c的具体内容:
/*1. 模块的头文件*/
#include <linux/init.h>
#include <linux/module.h>

static int a=1;
static char *str = "hello world";


/*2. 模块加载函数*/
static int __init  hello_init(void)
{
    printk(KERN_INFO"%s():%d\n", __func__,__LINE__);
    printk(KERN_INFO"%s(): a=%d  str=%s\n", __func__, a, str);
    return 0;
}

/*3. 模块卸载函数*/
static void __exit hello_exit(void)
{
    printk(KERN_INFO"%s():%d\n", __func__,__LINE__);
}

/*
**    param1: 变量
**  param2: 变量类型
**  param3: 权限
*/
module_param(a,int,S_IRWXU);
module_param(str,charp,S_IRWXU);


/*4. 模块许可声明*/
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("chenjun");
MODULE_VERSION("v1.0");
MODULE_DESCRIPTION("This is a simple driver module!");
MODULE_LICENSE("GPL");


    3,Makefile的编写
        //首先一进来就是先判断KERNELRELEASE  是否为空? KERNELRELEASE 在内核源码里应该有定义,
        //第一个进来编译时KERNELRELEASE  一定是为空的。
        ifeq ($(KERNELRELEASE),)
        //同样,KERNELDIR 也是定义为当前内核源码所在的路径
KERNELDIR = /home/chen/xxx/drive/linux-3.0.8
        //PWD 为当前要编译的代码的路径
PWD =$(shell pwd)
        //modules 是一个伪规则
        //值得一说的是:make -C    的含义,是编译指定目录下的某个文件。M表示为指定目录下的模块
以下是 Makefile的具体内容:
ifeq ($(KERNELRELEASE),)//变量
    KERNELDIR =/home/chen/work/kernel/linux-3.0.8   ###将编译好的内核路径赋值给KERNELDIR
    PWD =$(shell pwd)
modules:  ###进入内核路径,获取其中的Makefile,利用该Makefile编译所有的*.c--->*.o文件,同时将KERNELRELEASE赋值
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    cp hello.ko /opt/filesystem/s5pc100
modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
    rm -rf *.so *.o *.ko  .tmp_versions *.mod.c *.order *.symvers 
else
    obj-m :=hello.o  ###这个比较重要,意思是说把当前的那个.c文件编译成  ko文件
endif



           
        说明:实际上,在做一次make 去编译一个驱动文件,会进入2次Makefile。
    4,在目标板上在运行ko文件
        a,   insmod  xxx.ko    ,当然了,如果这个驱动是要手动去创建设备节点的话,在这之前还得手动去创建设备节点。
        例如:
               手动创建设备节点:
                mknod  /dev/hello   c  250  0
                
注释:mknod 为创建设备节点命令;
           /dev/hello 是说创建一个名为hello的设备节点在/dev目录下;
           c  即表示是字符设备
           250  表示主设备号
           0      表示次设备号
       但是今天我们这个是简单的例子,只在加载和卸载驱动文件时打印一句话,就没必要去创建设备节点,以下权当是明天的储备知识点来看。
二、设备节点
    1,作用:为应用提供访问接口。
        为什么:一般地,系统会将所有的字符驱动当作对像来看待,既然是对像,那么就有  属性和方法。
        所以说,我们一般会是以面向对像的方法去编写驱动文件。
        但是,又说:几乎所有的对像皆文件。所以说,其实就是对文件的操作。
        
    2,驱动要和应用空间打交道
        a,驱动需要申请号码。
        b,要有一个文件存在,将应用和驱动关联起来。
        c,应用如果去操作驱动(驱动要提供操作方法)。
        一、驱动需要申请号码。
            静态申请设备号并注册驱动到系统中:
                register_chrdev(主设备号,"驱动名称",&dev_fops(操作方法接口));
        二、要有一个文件存在,将应用和驱动关联起来
            第一天,我们就用手动创建设备节点(文件)的方法来做。
        三、就是在应用上对驱动操作了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
模块一般用来支持那些不经常使用的功能。例如,通常情况下你仅使用拨号网络,因此网络功能并不是任何时候都需要的,那么就应该使用可装入的模块来提供这个功能。仅在你进行拨号联接的时候,该模块才被装入。而在你断掉连接的时候它会被自动卸下。这样会使内核使用内存的量最小,减小系统的负荷。 当然,那些象硬盘访问这样时时刻刻都需要的功能,则必须作在内核里。如果你搭一台网络工作站或 web服务器,那么网络功能是时刻都需要的,你就应该考虑把网络功能编译到内核里。另外一个方法是在启动的时候就装入网络模块。这种方法的优点是你不需要重新编译内核。而缺点是网络功能不能特别高效。 按照以上的原则,我们首先列出一张清单,看看 kernel 中哪些选项是非有不可的,也就是说,这些东西是必须被编译到内核中的。将那些非必需的模块剔除到内核以外。 第一个是root所在的硬盘配置。 哪果您的硬盘是IDE接口,就把 ide 的选项标记下来。如果是SCSI接口,请把您的接口参数及 SCSI id 记标下来。 第二个是选择使用哪一个文件系统。 Linux的默认文件系统是是 ext2 ,那么就一定要把它标记下来。如果机器中还其它的操作系统,如win98或windows NT,您还会可能选择FAT32或NTFS的支持,不过后面你可以通过手工加载的方式来加入新的模块支持。 第三个是选择Linux所支持的可执行文件格式。这里有两种格式可供选择: elf:这是当前Linux普遍支持的可执行文件格式,必须编译到内核中 。 a.out: 这是旧版的Linux的可执行文件各函数库的格式,如果你确认肯定用不到这种格式的可执行文件,那么就可以不把它编译到内核当中。 以上这些内容,是必须要编译到内核中的。其它的内容凡是所有选项中m提示的,都选择m,这样可以通过手工的方式添加该模块。 ** Loadable module support*Enable loadable module support (CONFIG_MODULES) [Y/n/?]Set version information on all symbols for modules (CONFIG_MODVERSIONS) [N/y/?]Kernel daemon support (e.g. autoload of modules) (CONFIG_KERNELD) [Y/n/?] 分别回答 Y,N,Y 。其中 CONFIG_KERNELD 的 default 值是 N, 所以要注意选择Y。 make config 完后,仍旧是 make dep; make clean。 接下来要 make zlilo 或 make zImage。 然后 make modules ; make modules_install 。完成之后,就编译出一个没有调入多余模块的一个“干净的”内核映像文件了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值