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. 面向对象_06【抽象类:abstract、接口:interface、实现:implements】

    抽象类:abstract抽象:没有足够的描述功能,事物不具体的描述,却又有共性. 特点: 1,方法只有声明没有实现时,该方法就是抽象方法,需要被abstract修饰,抽象方法必须定义在抽象类中,该类也 ...

  2. 查看Eclipse运行工程时使用的Command Line

    一.查看使用的Command Line 1.Window -> Show View -> Other... -> Debug 2.运行工程,然后在Debug视窗中找到运行的工程的主线 ...

  3. Tensorflow ActiveFunction激活函数解析

    Active Function 激活函数 原创文章,请勿转载哦~!! 觉得有用的话,欢迎一起讨论相互学习~Follow Me Tensorflow提供了多种激活函数,在CNN中,人们主要是用tf.nn ...

  4. 09_Python深拷贝、浅拷贝

    一.循环列表,删除其中的元素 l1 = [1,2,3,4,5,6,7]  循环删除奇数位元素 1.正序循环删除,会出现越界情况,所以采用倒叙的方式删除 l1 = [1,2,3,4,5,6,7] for ...

  5. inotify-tools使用方法详解

    inotify-tools 是为linux下inotify文件监控工具提供的一套c的开发接口库函数,同时还提供了一系列的命令行工具,这些工具可以用来监控文件系统的事件. inotify-tools是用 ...

  6. BZOJ 4407: 于神之怒加强版 [莫比乌斯反演 线性筛]

    题意:提前给出\(k\),求\(\sum\limits_{i=1}^n \sum\limits_{j=1}^m gcd(i,j)^k\) 套路推♂倒 \[ \sum_{D=1}^n \sum_{d|D ...

  7. XSD详解三 - 复合元素+总结篇

    一.复合元素介绍 1.什么是复合元素? 复合元素指包含其他元素及/或属性的 XML 元素. 有四种类型的复合元素: 空元素 包含其他元素的元素 仅包含文本的元素 包含元素和文本的元素 注释:上述元素均 ...

  8. python如何讲一个文件中的图片分到两个

    最近在做一个图像分类的比赛,作为初次接触深度学习的菜鸟,上手了keras.说实话,除了keras教程,中文博客的技术支持太差了.正在头大的学习中...废话不多说,记录一下学习中的一些小细节.在遇到ge ...

  9. Ubuntu下Nginx启动、停止等常用命令

    本文详细介绍Ubuntu下Nginx启动.停止等常用命令.在开发过程中,我们会经常的修改Nginx的配置文件,每次修改配置文件都可以先测试下本次修改的配置文件是否正确,可以利用以下命令: servic ...

  10. Windows 定时任务对数据库进行操作

    定时对数据库进行操作可以用mysql的event事件来完成,但是只有mysql5.1后的才支持,所以有一定的局限性,也可以通过其他的mysql管理软件实现,而我发现Windows本身就有个定时任务的功 ...