Linux下的驱动程序也没有听上去的那么难实现,我们可以看一下helloworld这个例子就完全可以了解它的编写的方式!

我们还是先看一个这个例子,helloworld

1. [代码]helloworld.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <linux/module.h>//与module相关的信息
 
#include <linux/kernel.h>
#include <linux/init.h>      //与init相关的函数
 
static int __init hellokernel_init(void)
{
        printk(KERN_INFO "Hello kernel!\n");
        return 0;
}
 
static void __exit hellokernel_exit(void)
{
        printk(KERN_INFO "Exit kernel!\n");
}
 
 
module_init(hellokernel_init);
module_exit(hellokernel_exit);
 
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxx");

2. [代码]Makefile

1
2
3
4
5
6
7
8
9
obj-m := helloworld.o
 
PWD       := $(shell pwd)
 
all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules #TAB not space
 
clean:
        rm -rf *.o *~ core .*.cmd *.mod.c ./tmp_version

3. [代码]执行与运行结果     跳至 [1] [2] [3] [全屏预览]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
3)执行make
 
编译成功之后会生成相应有ko文件,也就是我们想要的驱动了
 
4)驱动程序的相关操作
 
      a)查看ko模块的信息 modinfo
 
      b)插入模块 insmod helloworld.ko
 
      c)卸载模块 rmmod helloworld
 
      d)还有一个modprobe功能,以后介绍!
 
5)查看驱动的打印信息
 
      使用dmesg可以查看在驱动的相关打印信息!
 
       现在有例子是会有如下的打印内容:
 
---------------------log start----------------------------
 
[27520.195551] Exit kernel!
[27948.531569] Hello kernel!
 
---------------------log end----------------------------

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

(1)驱动程序Hello.c的源代码:

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/version.h>

#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/device.h>    

MODULE_LICENSE("Dual BSD/GPL");

int Hello_read(struct inode *inode, struct file *filp)
{
	printk("enter cdd_open!\n");
	return 0;
}

int Hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
{
	printk("enter cdd_write!\n");
	return 0;
}

static struct file_operations io_dev_fops = {
    .owner = THIS_MODULE,
    .read = Hello_read,
    .write = Hello_write,
}; 

int __init hello_init(void)
{
    register_chrdev(123, "Hello",&io_dev_fops);
    printk("Ralink gpio driver initialized\n");
	return 0;
}

void __exit hello_exit(void)
{
    unregister_chrdev(123, "Hello");
    printk("Ralink hello driver exited\n");
}

module_init(hello_init);
module_exit(hello_exit);

驱动是靠近底层,与硬件和内核打交道的。内核通过驱动将请求转化为命令操作硬件设备。因此驱动程序向上要给内核提供接口,向下要能够读写硬件的寄存器,能够控制硬件。驱动首先要告诉内核它的存在,也就是注册。代码中的注册函数在下面这个函数中,同时这个函数就是这个驱动的入口,当我们利用insmod命令挂在驱动的时候,首先会进入到这个函数中: 

int __init hello_init(void)
{
    register_chrdev(123, "Hello",&io_dev_fops);
    printk("Ralink gpio driver initialized\n");
	return 0;
}

register_chrdev(123, "Hello",&io_dev_fops);123为主设备号“Hello”是在/proc/devices下面显示的该驱动的名称,io_dev_fops是一个file_operations结构体

static struct file_operations io_dev_fops = {
    .owner = THIS_MODULE,
    .read = Hello_read,
    .write = Hello_write,
}; 

这个结构体相当于一个连接,通过它可以找到在这个驱动中的其他的函数Hello_read、Hello_write(这两个函数分别对应着应用层程序中的函数write、read,当在应用层调用这两个函数,就会通过一系列的动作找到对应的驱动程序中的Hello_read、Hello_write函数),这函数里面就可以写一些对于硬件的操作,因为我写的是最简单的驱动程序,因此没有对于写硬件的操作。

下面是出口函数,当使用rmmod卸载函数时会进入到下面这个函数中

void __exit hello_exit(void)
{
    unregister_chrdev(123, "Hello");
    printk("Ralink hello driver exited\n");
}

unregister_chrdev(123, "Hello");是注销驱动程序的注册信息的。

每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。

一般说来,PCI卡通常都属于字符设备

(2)测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>       

int main()
{
    int fd;
    int value=1;
    fd=open("/dev/hello",O_RDWR);
    if (fd<0)
    {
        printf("open led_driver error");
        exit(1);
    }
    write(fd,&value,4);
    return 0;
 }  

(3)测试

1、利用tftp将文件下载到开发板上


后面的IP地址是tftp服务器的地址。

2、查看一下/proc/devices,没有此驱动程序要申请的主设备号123

3、利用insmod命令安装驱动后再次查看,有主设备号123 Hello,说明驱动安装成功,并且输出Ralink gpio driver initialized,说明进入到驱动的初始化函数中。

4、测试驱动

运行test发现出现错误:


查找原因是因为在/dev下没有设备节点/dev/Hello

利用mknod新建设备节点并再次查看/dev,有设备节点hello

再次运行test:

测试程序调用驱动成功,输出enter cdd_write,说明成功进入调用到Hello_write函数!

5、卸载驱动

到此为止,第一个驱动程序编写成功!

如何编译linux第一个模块 hellomod.ko的更多相关文章

  1. 简单实例讲解linux的module模块编译步骤

    注:原博文地址http://blog.sina.com.cn/s/blog_4ba5b45e0102v25h.html ---------------------------------------- ...

  2. Linux内核-模块编译和安装

    我安装Ubuntu的时候是没有安装源码的,在没有安装源码前 /usr/src/ 目录下是只有两个包含内核的头文件的文件夹的: 我的内核版本是: 所以接下来就是先安装内核源码: 执行后,/usr/src ...

  3. Linux操作系统内核编译之NTFS文件系统模块支持案例

    Linux操作系统内核编译之NTFS文件系统模块支持案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内核编译概述 单内核体系设计.但充分借鉴了微内核设计体系的优点,为内核引 ...

  4. Linux下编译安装Apache及模块

    Apache是时下最流行的Webserver软件之中的一个,支持多平台,可高速搭建web服务,并且稳定可靠.并可通过简单的API扩充.就能够集成PHP/Python等语言解释器. 文章这里解说怎样在l ...

  5. linux内核裁剪及编译可加载模块

    一:linux内核裁剪: 1:编译内核源码: 今天的重点内容是内核驱动的编写,在编写驱动之前首先的了解linux内核源码,linux主要是由五个子系统组成:进程调度,内存管理,文件系统,网络接口以及进 ...

  6. 编译linux kernel及制作initrd ( by quqi99 )

    编译linux kernel及制作initrd ( by quqi99 ) 作者:张华  发表于:2013-01-27    ( http://blog.csdn.net/quqi99 ) 运行一个l ...

  7. 编译linux内核以及depmod的使用

    转载:http://blog.lmtw.com/b/18215/archives/2010/71074.html depmod(depend module) 功能说明:分析可载入模块的相依性. 语 法 ...

  8. 如何解决编译linux内核(解决声卡问题),遭遇fatal error: linux/limits.h: 没有那个文件或目录

    最近帮一位上海的朋友搞一块小板,在ubuntu15.04 vivid上已经加载了对应了.ko驱动包 但关键是系统根本就枚举不到该声卡ALC5640,试了OpenSUSE也是一样的结果,看来是内核漏加载 ...

  9. linux中pam模块

    https://www.cnblogs.com/ilinuxer/p/5087447.html linux中pam模块 一.pam简介 Linux-PAM(linux可插入认证模块)是一套共享库,使本 ...

随机推荐

  1. java 对象、集合的非空判断

    自我总结,有什么不到位的地方,请各位纠正补充,感激不尽! 目的:使程序更严谨 ***对象验证是否不为空:  if( null != obj ) ***List验证不为空:if( null != lis ...

  2. Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. vc++调用web服务传输文件

    bool webService::UploadFile(LPWSTR appKey, LPWSTR fileName, const int len, unsigned char * buff) { t ...

  4. mvn 手动安装jar 到本地库

    安装: mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.1.0.7.0 -Dpackagi ...

  5. input里面placeholder水平居中

    <style type="text/css">input::-ms-input-placeholder{text-align: center;}input::-webk ...

  6. Hive on ES

    ES对于类似数据库的SQL查询很无力,可以使用Hive on ES来实现SQL的查询.2个百万级的索引做关联时,需要大概1分多钟,基于es2.1版本. 1.将elasticsearch-hadoop- ...

  7. 三栏布局,div左右盒子是定宽,中间是自适应

    用弹性布局flex: 给父盒子加个display:flex; 给中间盒子设flex=1; /* 弹性盒子布局*/ .wrap{ width: 100%; height: 90px; display: ...

  8. CSA单点登录环境配置

    本篇先写一些基础 今天看到一个cas单点登录的源码,搞环境就废了大半时间 <SSO CAS单点系列>http://www.imooc.com/article/3576 参考了这篇博客里的配 ...

  9. JAVAEE——BOS物流项目05:OCUpload、POI、pinyin4J、重构分页代码、分区添加、combobox

    1 学习计划 1.实现区域导入功能 n OCUpload一键上传插件使用 n 将文件上传到Action n POI简介 n 使用POI解析Excel文件 n 完成数据库操作 n 使用pinyin4J生 ...

  10. .35-浅析webpack源码之babel-loader入口文件路径读取

    在处理./input.js入口文件时,在类型判断被分为普通文件,所以走的文件事件流,最后拼接得到文件的绝对路径. 但是对应"babel-loader"这个字符串,在如下正则中被判定 ...