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. 多对多中间表详解 -- Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  2. Linux 下定时备份数据库以及删除缓存

    一.定时备份数据库 1.在根目录下创建备份文件夹 #mkdir backup 2.进入到该目录下,创建backup.sh文件 3.赋予文件权限让其变成可执行文件 4.在backup.sh中写备份的脚本 ...

  3. java基础(六) switch语句的深入解析

    引言   switch 语句是非常的基础的知识,掌握起来也不难掌握,语法比较简单.但大部分人基本是知其然,不知其所以然.譬如 早期JDK只允许switch的表达式的值 int及int类型以下的基本类型 ...

  4. ipython的用法详解

    ipython是一个升级版的交互式python命令行工具. ipython安装 pip install ipython 等到命令执行完成后显示successfully表示完装成功 在命令提示符下输入i ...

  5. java配置context.xml文件

    2018-02-08   23:32:23 修改context.xml文件 自从学习了servlet后,每次修改里面的内容后,想要访问都要重启服务器,这样感觉很麻烦的,所以今天就教大家一个方法,只需要 ...

  6. Typora最常用快捷键

    插入图片:直接拖动到指定位置即可或者ctrl+shift+i 插入链接:ctrl+k 对于本地图片,我们可以直接拖进来 双回车可以回到行首

  7. 使用WinInet实现HTTP站点访问

    废话不多说了,直接上代码 HTTP的GET方式代码 void sendGetRequest(LPCTSTR lpszURL) { LPCTSTR lpszAgent = _T("Winine ...

  8. 读书共享 Primer Plus C-part 9

    第十二章 存储类.链接和内存管理                                                       针对代码块中的static变量做如下范本 #include ...

  9. LeetCode - 661. Image Smoother

    Given a 2D integer matrix M representing the gray scale of an image, you need to design a smoother t ...

  10. [Python Study Notes]CS架构远程访问获取信息--Client端v2.0

    更新内容: 1.增加内存信息获取 2.增加电池信息获取 3.增加磁盘信息获取 4.重新布局窗体 5.增加窗体名称 6.增加连接成功之前,不可按压 效果图: '''''''''''''''''''''' ...